Code Doodles!
- lazershark3k
- Prole
- Posts: 27
- Joined: Tue Apr 10, 2018 3:20 pm
Re: Code Doodles!
The A is a point;
Line B12 is a sector
dx dy is a direction (and length) of ray from point A
The result: the point A will be projected to the line B12.
Line B12 is a sector
dx dy is a direction (and length) of ray from point A
The result: the point A will be projected to the line B12.
Code: Select all
local function findIntersectionPoint(ax, ay, b1x, b1y, b2x, b2y, dx, dy)
local segmentX = b2x - b1x
local segmentY = b2y - b1y
local determinant = dy * segmentX - dx * segmentY
if determinant == 0 then
return nil
end
local t = ((b1y - ay) * segmentX - (b1x - ax) * segmentY) / determinant
if t < 0 then -- wrong side of the ray
return nil
elseif t > 1 then -- too far; the ray is too short
-- do nothing; keep projecting
else -- t is between 0 and 1
-- do nothing
end
local u = ((ax - b1x) * dy - (ay - b1y) * dx) / determinant
if u < 0 or u > 1 then
-- out of segment
return nil
end
dx = t*dx
dy = t*dy
local length = math.sqrt(dx*dx+dy*dy)
return ax+dx, ay+dy, dx, dy, length
end
Re: Code Doodles!
Hi darkfrei, Please read the first post, then look at the doodles that others have posted. Thanks.
Re: Code Doodles!
Sorry pgimeno!
The code handles the movement of a point along a line segment while taking collision into account.
The point is represented by its current position (x, y), target position (tx, ty), and speed. The line segment is defined by its endpoints (x1, y1) and (x2, y2).
This code demonstrates how to handle point-to-segment collision. It can be useful in various applications involving collision detection and movement along defined paths.
Update:
sliding functions:
The code handles the movement of a point along a line segment while taking collision into account.
The point is represented by its current position (x, y), target position (tx, ty), and speed. The line segment is defined by its endpoints (x1, y1) and (x2, y2).
This code demonstrates how to handle point-to-segment collision. It can be useful in various applications involving collision detection and movement along defined paths.
Code: Select all
point = {x=200, y=200, tx=100, ty=100, speed=200}
segment = {100, 400, 600, 100}
local function handlePointCollision(px, py, x1, y1, x2, y2, dx, dy)
local segmentDX = x2 - x1
local segmentDY = y2 - y1
local segmentLength = math.sqrt(segmentDX * segmentDX + segmentDY * segmentDY)
segmentDX = segmentDX / segmentLength
segmentDY = segmentDY / segmentLength
local dotProduct = (px - x1) * segmentDY - (py - y1) * segmentDX
local dotProduct2 = (px + dx - x1) * segmentDY - (py + dy - y1) * segmentDX
if not (dotProduct > -1/256 and dotProduct2 < 0) then
-- not crossing segment from positive to negative side
return dx, dy
end
local segmentDot = (px - x1) * (x2 - x1) + (py - y1) * (y2 - y1)
local t = segmentDot / (segmentLength * segmentLength)
if t >= 0 and t <= 1 then
-- collision with segment
dx = (x1 + segmentLength*segmentDX * t)-px
dy = (y1 + segmentLength*segmentDY * t)-py
love.window.setTitle (dx..' '..dy)
return dx, dy
else
-- no collision with segment
return dx, dy
end
end
function love.update(dt)
local px, py = point.x, point.y
local speed = point.speed
local dx0, dy0 = point.tx-point.x, point.ty-point.y
local angle = math.atan2 (dy0, dx0)
local dx = dt*speed*math.cos (angle)
local dy = dt*speed*math.sin (angle)
if math.abs (dx0) < math.abs(dx)
or math.abs (dy0) < math.abs(dy) then
dx = dx0
dy = dy0
end
local x1, y1 = segment[1], segment[2]
local x2, y2 = segment[3], segment[4]
dx, dy = handlePointCollision(px, py, x1, y1, x2, y2, dx, dy)
point.x, point.y = px+dx, py+dy
end
function love.draw()
love.graphics.line (segment)
love.graphics.circle ('fill', point.x, point.y, 5)
love.graphics.circle ('line', point.tx, point.ty, 7)
love.graphics.line (point.x, point.y, point.tx, point.ty)
end
function love.mousemoved( x, y)
point.tx = x
point.ty = y
end
sliding functions:
Code: Select all
point = {x=200, y=200, tx=100, ty=100, speed=200}
segment = {200, 200, 400, 100}
local function sticking (px, py, x1, y1, x2, y2, t)
local segmentDX = x2 - x1
local segmentDY = y2 - y1
local dx = (x1 + segmentDX * t) - px
local dy = (y1 + segmentDY * t) - py
return dx, dy
end
local function sliding (segmentDX, segmentDY, dx, dy)
local dotProduct = dx * segmentDX + dy * segmentDY
dx = segmentDX * dotProduct
dy = segmentDY * dotProduct
return dx, dy
end
local function handlePointCollision(px, py, x1, y1, x2, y2, dx, dy)
local segmentDX = x2 - x1
local segmentDY = y2 - y1
local segmentLength = math.sqrt(segmentDX * segmentDX + segmentDY * segmentDY)
segmentDX = segmentDX / segmentLength
segmentDY = segmentDY / segmentLength
local dotProduct = (px - x1) * segmentDY - (py - y1) * segmentDX
local dotProduct2 = (px + dx - x1) * segmentDY - (py + dy - y1) * segmentDX
if not (dotProduct > -1/256 and dotProduct2 < 0) then
-- not crossing segment from positive to negative side
return dx, dy
end
local segmentDot = (px - x1) * (x2 - x1) + (py - y1) * (y2 - y1)
local t = segmentDot / (segmentLength * segmentLength)
if t >= 0 and t <= 1 then
-- collision with segment
local isSliding = true
if isSliding then
return sliding (segmentDX, segmentDY, dx, dy)
else
return sticking (px, py, x1, y1, x2, y2, t)
end
else
-- no collision with segment
return dx, dy
end
end
function love.update(dt)
local px, py = point.x, point.y
local speed = point.speed
local dx0, dy0 = point.tx-point.x, point.ty-point.y
local angle = math.atan2 (dy0, dx0)
local dx = dt*speed*math.cos (angle)
local dy = dt*speed*math.sin (angle)
if math.abs (dx0) < math.abs(dx)
or math.abs (dy0) < math.abs(dy) then
dx = dx0
dy = dy0
end
local x1, y1 = segment[1], segment[2]
local x2, y2 = segment[3], segment[4]
dx, dy = handlePointCollision(px, py, x1, y1, x2, y2, dx, dy)
point.x, point.y = px+dx, py+dy
end
function love.draw()
love.graphics.line (segment)
love.graphics.circle ('fill', point.x, point.y, 5)
love.graphics.circle ('line', point.tx, point.ty, 7)
love.graphics.line (point.x, point.y, point.tx, point.ty)
end
function love.mousemoved( x, y)
point.tx = x
point.ty = y
end
- Attachments
-
- point-line-collision-02.love
- (987 Bytes) Downloaded 381 times
Re: Code Doodles!
The code can be useful for generating and visualizing slot car track layouts with different parameters:
Code: Select all
-- License CC0 (Creative Commons license) (c) darkfrei, 2023
love.window.setMode(1280, 800) -- Steam Deck resolution
love.window.setTitle("Slot Car Track")
local function setLastPoint (x, y, angle)
LastPoint = {x=x, y=y, angle = math.rad (angle)}
end
local function addStraightRoad (length, logRoads)
if logRoads then
print ('Straight', 'length: ' .. length)
end
local x1, y1 = LastPoint.x, LastPoint.y
local angle = LastPoint.angle
local x2 = x1 + length*math.cos (angle)
local y2 = y1 + length*math.sin (angle)
if logRoads then
x2 = math.floor(x2+0.5)
y2 = math.floor(y2+0.5)
end
local road = {}
-- line to render
road.render = {}
road.render.line = {x1, y1, x2, y2}
road.render.circles = {{x1, y1}, {x2, y2}}
LastPoint.x = x2
LastPoint.y = y2
LastPoint.angle = math.atan2 (y2-y1, x2-x1)
table.insert (Track, road)
end
local function addCurvedRoad(radius, angle, logRoads)
if logRoads then
print ('Curved', 'radius: ' .. radius, 'angle: ' .. angle)
end
-- starting point
local x1, y1 = LastPoint.x, LastPoint.y
local angle1 = LastPoint.angle
local angleSign = angle > 0 and 1 or -1
angle = math.rad(angle)
local angle2 = angle1 + angle
-- Middle point
local xc = x1 - angleSign * radius * math.sin(angle1)
local yc = y1 + angleSign * radius * math.cos(angle1)
-- end point
local x2 = xc + angleSign * radius * math.sin(angle2)
local y2 = yc - angleSign * radius * math.cos(angle2)
if logRoads then
x2 = math.floor(x2+0.5)
y2 = math.floor(y2+0.5)
end
-- control factor:
local k = math.abs (4/3*math.tan(angle / 4))
-- print (v1)
local cp1x = x1 + k * radius * math.cos(angle1)
local cp1y = y1 + k * radius * math.sin(angle1)
local cp2x = x2 - k * radius * math.cos(angle2)
local cp2y = y2 - k * radius * math.sin(angle2)
local road = {}
road.render = {}
-- road.render.circles = {{xc, yc}, {x1, y1}, {x2, y2}}
road.render.circles = {{x1, y1}}
-- road.render.line = {xc, yc, x1, y1, cp1x, cp1y, cp2x, cp2y, x2, y2, xc, yc}
local curve = love.math.newBezierCurve( x1, y1, cp1x, cp1y, cp2x, cp2y, x2, y2)
road.render.line = curve:render()
LastPoint.x = x2
LastPoint.y = y2
LastPoint.angle = angle2
table.insert(Track, road)
end
local function createTrack (r0, logRoads)
Track = {}
setLastPoint (500, 700, 0)
local r1, a1 = r0*2, 30
local r2, a2 = r0*(2^0.5), 30
-- local r2, a2 = r0*1.4142, 30
-- local r2, a2 = r0*1.53, 30
local r3, a3 = r0, 60
addStraightRoad (141, logRoads)
addCurvedRoad (r1, -a1, logRoads)
addCurvedRoad (r1, -a1, logRoads)
addCurvedRoad (r1, -a1, logRoads)
addCurvedRoad (r2, -a2, logRoads)
addCurvedRoad (r2, -a2, logRoads)
addCurvedRoad (r2, -a2, logRoads)
addCurvedRoad (r2, -a2, logRoads)
addCurvedRoad (r1, a1, logRoads)
addCurvedRoad (r3, a3, logRoads)
addCurvedRoad (r3, a3, logRoads)
addCurvedRoad (r2, a2, logRoads)
addCurvedRoad (r2, a2, logRoads)
addCurvedRoad (r1, a1, logRoads)
addStraightRoad (200, logRoads)
addCurvedRoad (r2, a2, logRoads)
addCurvedRoad (r3, a3, logRoads)
addCurvedRoad (r3, a3, logRoads)
addStraightRoad (400, logRoads)
addCurvedRoad (r3, -a3, logRoads)
addCurvedRoad (r3, -a3, logRoads)
addCurvedRoad (r3, -a3, logRoads)
addStraightRoad (100, logRoads)
-- print (Track[1].render.line[1], Track[1].render.line[2])
-- print (LastPoint.x, LastPoint.y)
local dy = Track[1].render.line[2] - LastPoint.y
local dx = Track[1].render.line[1] - LastPoint.x
-- print ('dy:', dy)
return dy, dx
end
local function findXByBisection(func, y, xMin, xMax, tolerance)
local xMid = (xMin + xMax) / 2
local yMid = func(xMid)
local lastXMid = nil
while math.abs(yMid - y) > tolerance do
if (yMid - y) * (func(xMin) - y) > 0 then
xMin = xMid
else
xMax = xMid
end
xMid = (xMin + xMax) / 2
yMid = func(xMid)
print (xMid, yMid)
if lastXMid == xMid then
return xMid
end
lastXMid = xMid
end
print ('error: ', math.abs(yMid - y), yMid, y)
-- return math.floor(xMid+0.5)
return xMid
end
-- Пример использования
local targetY = 0
local xMin, xMax = 0, 600
local tolerance = 0.00001
--local radius = findXByBisection(createTrack, targetY, xMin, xMax, tolerance)
local radius = 113.85440826416
print(radius) -- Вывод найденного значения x
function love.load()
local xMin = 10
local xMax = 300
local dy, dx = createTrack (radius, true)
print ('dx: ' .. dx, 'dy: ' .. dy)
end
function love.update(dt)
end
function love.draw()
love.graphics.setColor (1,1,1)
for _, road in ipairs (Track) do
local r = road.render
if r.line then
love.graphics.line (r.line)
end
if r.circles then
for _, point in ipairs (r.circles) do
love.graphics.circle ('line', point[1], point[2], 4)
end
end
end
love.graphics.setColor (1,1,0)
love.graphics.line (LastPoint.x, LastPoint.y,
LastPoint.x + 50*math.cos (LastPoint.angle),
LastPoint.y + 50*math.sin (LastPoint.angle))
love.graphics.circle ('fill', LastPoint.x, LastPoint.y, 3)
love.graphics.circle ('fill', LastPoint.x + 50*math.cos (LastPoint.angle), LastPoint.y + 50*math.sin (LastPoint.angle), 3)
end
Re: Code Doodles!
Tiled polygons, rethinked and optimized to the doodle:
Code: Select all
-- License CC0 (Creative Commons license) (c) darkfrei, 2023
-- tiled polygons v. 2023-07-12
love.window.setTitle ('Tiled Polygons 2023-07-12')
Tiles = {}
Size = 4
local v1, v2, v3, v4 = 1, 1+1*Size, 1+2*Size, 1+3*Size
for y = 0, 2*Size-1 do
for x = 0, 2*Size-1 do
local absDif = math.abs (y-x)
if absDif < Size then
if x == 0 and y == 0 then
-- square
local tile = {v1, v2, v3, v4}
tile.x, tile.y = x, y
table.insert (Tiles, tile)
elseif y == 0 then
-- rectangle
local tile = {v1, v2-x, v3+x, v4}
print (x, y, table.concat (tile, ','))
tile.x, tile.y = x, y
table.insert (Tiles, tile)
elseif x == 0 then
-- rectangle
local tile = {v1, v2, v3-y, v4+y}
print (x, y, table.concat (tile, ','))
tile.x, tile.y = x, y
table.insert (Tiles, tile)
else
local tile = {v1}
local vx = v3-x
local vy = v3+y
if vx > v2 then
table.insert (tile, v2)
end
table.insert (tile, vx)
table.insert (tile, vy)
if vy < v4 then
table.insert (tile, v4)
end
print (x, y, table.concat (tile, ','))
tile.x, tile.y = x, y
table.insert (Tiles, tile)
end
end
end
end
Vertices = {}
for i = 1, Size do
local t1 = (i - 1)/Size
Vertices[i] = {x=t1, y=0}
Vertices[i + Size] = {x=1, y=t1}
Vertices[i + Size * 2] = {x=1-t1, y=1}
Vertices[i + Size * 3] = {x=0, y=1-t1}
end
for i, v in ipairs (Vertices) do
print (i, v.x, v.y)
end
Polygons = {}
for i, tile in ipairs (Tiles) do
local x = 1.25*tile.x
local y = 1.25*tile.y
local vertices = {}
print ('{'..table.concat (tile, ',')..'}, -- ' .. i)
for j, vIndex in ipairs (tile) do
local x1 = x + Vertices[vIndex].x
local y1 = y + Vertices[vIndex].y
table.insert (vertices, x1)
table.insert (vertices, y1)
end
table.insert (Polygons, vertices)
end
function love.draw ()
love.graphics.translate (10, 10)
local scale = 58
love.graphics.scale (scale)
love.graphics.setLineWidth (2/scale)
for i, vertices in ipairs (Polygons) do
love.graphics.polygon ('line', vertices)
end
end
- Attachments
-
- main.lua
- (2.1 KiB) Downloaded 347 times
Re: Code Doodles!
Poisson Discs on Toroidal World:
Special for Craquelure viewtopic.php?p=255763#p255763
Example result:
Please find where the image repeating itself:
Special for Craquelure viewtopic.php?p=255763#p255763
Code: Select all
-- cc0, 2023, darkfrei
local function poissonDiscs(gridWidth, gridHeight)
-- poisson discs in toroidal grid
local cellSize = 1
local radius = math.sqrt(2)
local distanceMin = radius
local distanceMax = 1.5 * radius
local samples = 32
local lines = {}
local grid = {} -- in format cell = grid[cy][cx] and cell = {cx=cx, cy=cy}
for cy = 1, gridHeight do
grid[cy] = {}
for cx = 1, gridWidth do
grid[cy][cx] = {} -- empty cell
end
end
local activeCells = {} -- active cells
local resultPoints = {} -- result points as {x1, y1, x2, y2 ...}
local function getDistance(x1, y1, x2, y2)
-- Calculate the toroidal distance between two points
local dx = math.abs(x2 - x1)
local dy = math.abs(y2 - y1)
local distanceX = math.min(dx, gridWidth - dx)
local distanceY = math.min(dy, gridHeight - dy)
local distance = math.sqrt(distanceX ^ 2 + distanceY ^ 2)
-- print (math.floor (distance+0.5))
return math.sqrt(distanceX ^ 2 + distanceY ^ 2)
end
local function insertPoint(x, y)
-- Insert a new point into the grid and active cells
local cx = (math.floor (x))%gridWidth+1
local cy = (math.floor (y))%gridHeight+1
if not grid[cy][cx].x then
local cell = {x = x, y = y}
grid[cy][cx] = cell
table.insert(activeCells, cell)
table.insert(resultPoints, x)
table.insert(resultPoints, y)
end
end
local function isPointValid(x, y)
-- Check if a cell is valid for point placement
local cx = math.floor(x) + 1
local cy = math.floor(y) + 1
local cell = grid[cy][cx]
if cell.cx ~= nil and cell.cy ~= nil then
return false
end
for dy = -2, 2 do
for dx = -2, 2 do
local neighborCX = (cx + dx - 1) % gridWidth + 1
local neighborCY = (cy + dy - 1) % gridHeight + 1
if not ((cx == neighborCX) and (cy == neighborCY)) then
local neighbor = grid[neighborCY][neighborCX]
if neighbor.x then
local distance = getDistance(x, y, neighbor.x, neighbor.y)
-- print (distance, distanceMin)
if distance < distanceMin then
return false
end
end
end
end
end
return true
end
-- first point, if anywhere, why not 0,0?
insertPoint(0, 0)
while #activeCells > 0 do
local randomIndex = math.random(#activeCells)
local cell = table.remove(activeCells, randomIndex)
local x, y = cell.x, cell.y
local cx, cy = math.floor (x) + 1, math.floor (y) + 1
for _ = 1, samples do
local angle = 2 * math.pi * math.random()
local distance = distanceMin + math.random() * (distanceMax - distanceMin)
local offsetX = math.cos(angle) * distance
local offsetY = math.sin(angle) * distance
local newX = (x + offsetX) % gridWidth
local newY = (y + offsetY) % gridHeight
if isPointValid(newX, newY) then
insertPoint(newX, newY)
table.insert (lines, {x, y, (x + offsetX), (y + offsetY)})
end
end
end
print ('cells:', gridWidth, gridHeight, gridWidth * gridHeight)
print ('resultPoints', #resultPoints/2)
return resultPoints, lines
end
function love.load ()
GridWidth, GridHeight = 30, 20
Points, Lines = poissonDiscs(GridWidth, GridHeight)
Scale = math.floor (math.min (love.graphics.getWidth()/GridWidth, love.graphics.getHeight()/GridHeight))/2
end
function love.draw()
love.graphics.scale (Scale)
love.graphics.setPointSize (3)
love.graphics.setLineWidth (0.25/Scale)
love.graphics.translate (GridWidth/2, GridHeight/2)
for dy = -GridHeight, GridHeight, GridHeight do
for dx = -GridWidth, GridWidth, GridWidth do
love.graphics.push()
love.graphics.translate (dx, dy)
for y = 1, GridHeight do
for x = 1, GridWidth do
if (x+y)%2 == 0 then
love.graphics.setColor (0.2,0.2,0.2)
else
love.graphics.setColor (0.3,0.3,0.3)
end
love.graphics.rectangle ('fill', x-1, y-1, 1,1)
end
end
love.graphics.pop()
end
end
for dy = -GridHeight, GridHeight, GridHeight do
for dx = -GridWidth, GridWidth, GridWidth do
love.graphics.push()
love.graphics.translate (dx, dy)
love.graphics.setColor (0.5,0.5,0.5)
for y = -1, 1 do
for x = -1, 1 do
for i = 1, #Points-1, 2 do
love.graphics.circle ('line', Points[i], Points[i+1], math.sqrt(2))
end
end
end
love.graphics.setColor (1,1,1)
love.graphics.points (Points)
love.graphics.pop()
end
end
end
function love.keypressed(key, scancode, isrepeat)
if key == "escape" then
love.event.quit()
end
end
Example result:
Code: Select all
{0,0,29.421497428895,18.262284985482,1.9791994775338,0.71104989006247,28.936347589706,1.3874202099944,0.77918559894233,1.7119154545849,0.74872496156755,18.75908887808,2.4294796576936,18.621404096665,0.74629667130324,17.272948180996,27.224705013952,0.60592345728928,28.113281538712,2.7081013486532,27.286359005096,4.183978333816,29.758882374693,3.3617643405968,26.731307726236,2.3346689791489,2.1391061763197,2.3662886471186,26.11672261621,18.802623146342,25.427344998163,0.35620449316817,28.21890597267,19.192023390212,3.7517173300831,1.7900008652362,1.981748592693,3.7754927204595,3.6491112562124,19.761768053947,4.674214819273,2.9490230330072,5.6408681601946,0.93025608865254,25.153278935918,3.5477422539136,24.74775908144,1.7408658927981,3.6468147671778,17.686777140792,2.7256975440463,16.586500273227,27.353484813482,17.711706794635,3.5807610961327,15.227472542947,1.447531381856,15.587596556207,4.8298954808599,16.620574836613,0.31465693871064,14.276126652375,29.511215767343,15.609803506531,2.1962809166391,13.945271943441,3.2672962025191,4.6199685102339,1.4474853623782,5.5148168661481,29.694158401428,5.2731956805743,5.37416143966,18.912707009383,26.946086120639,5.9227939397719,4.6932753886167,14.330123415792,6.9782426673136,18.413258136099,6.3445081320094,17.066505830656,8.2903487194649,19.086285734748,7.0252711656188,0.3873973193782,8.4495737245075,17.180138859889,24.698151332349,18.85177868671,26.012341279164,16.96074196664,28.960775498939,13.336494835331,0.90739685240295,12.873455631666,22.917225458181,19.363868669458,24.275265008088,17.465685829653,23.929765725272,0.54500552563436,6.7168409399952,2.1326746080956,9.098337121903,0.33492806859019,28.205328998772,14.904968278816,28.647034910054,16.852242409054,23.432729781527,4.2211867275551,23.574917883008,2.7937404923595,24.581165813541,5.5119634572548,0.68237096898904,7.1810494623508,2.2801117761457,7.2564079804841,3.1735159901111,6.11318099228,4.8951935725097,5.7195046128446,4.2766502409951,7.1216422599576,28.389755385136,6.248663557283,27.812609649106,7.7795543089628,25.39745634073,6.7148584441762,25.946492069892,4.8971573573492,26.243061820043,9.2012615989797,28.92375476746,8.9997849663966,6.6697296925175,13.811078483549,5.5586046248046,12.823025353141,3.9743078751966,12.610703282608,6.098882769853,15.109111748691,22.002857044804,2.6583741071336,22.509223849567,5.8222648100415,21.879813738722,4.1833119344917,24.406180342051,8.4994367798685,23.634969557267,7.3137831643219,27.373594538849,16.05002538136,23.448567626534,16.111926027443,22.366804890887,17.568268526608,24.958511918221,15.851389585741,9.8301356791575,18.572545833927,8.0128961369235,15.667830174963,10.113043830961,16.380111796853,9.11275904049,14.720274645358,7.5952044091645,12.660629455291,6.5275294429725,11.165210286208,4.5163089839191,11.071709542587,20.908047017226,5.938537862401,19.903335463998,4.4204289956641,20.608301362255,3.0710626746245,21.67628922207,0.090076372672286,22.760776162152,1.3969603245843,20.182403809908,7.284104666209,19.025181940175,6.0634745760051,21.782208297784,7.0547801776248,18.224622922412,7.8673727956669,17.378466772205,5.8153715963749,18.350488480217,4.1961035468554,21.757523856438,8.5664149354302,27.445342424373,10.133788761031,26.249118878489,10.922312935011,24.495579459621,9.9124655327756,11.594327665859,17.521496729008,11.71943630784,15.251330921105,16.448616033666,8.0877722215436,18.357595870684,9.81539689341,19.692768502809,8.9494817952152,29.527524694761,10.363938169432,0.28983215537493,8.5450233694436,25.945401964827,14.654425062408,17.101329771224,11.063643512431,19.927578543818,10.421911618694,18.851751125117,11.648940994635,16.611875213448,9.575802384804,9.5716853301274,1.6922636139561,8.0456709977104,1.6256254828659,10.933816062008,0.21014898415188,6.288352782026,3.6513728765422,1.6422720181125,9.9966177685061,8.1382361210998,10.737822980767,5.5290028087227,9.5324793548481,21.952304906453,10.723848774477,20.438720270184,12.123753127681,7.8337603688152,3.1691685127183,26.971947966862,13.660304229387,25.532421620072,12.959410658719,23.090454090237,9.3014071791645,17.562995943229,12.714553353205,19.046232707164,13.622709477516,7.1093722630043,5.5013454945225,9.5204935705948,13.318247355303,7.4576655393904,9.491729111497,3.6229295308856,9.0564833977333,5.7374655643323,7.8175367520628,3.1381991278438,10.393587001011,28.415378082348,11.64111327503,2.4157351681502,11.969362611613,17.740948964424,14.31840251801,19.804486852267,14.977275712126,20.834973898727,13.923332868907,18.488724639474,15.891118140154,20.429027062187,16.315774547216,16.154560697578,15.53719982925,15.763623513407,13.958621458879,15.142017683596,10.93762742525,15.843302664241,12.459627475133,22.477959557257,12.332835964072,20.228447124499,17.868882186928,23.354199318503,10.982858873003,24.357392061813,14.346981952072,18.533816474532,17.333983176357,17.231068059174,16.653718869598,22.545180836352,13.80445153977,24.219511279903,12.409347950979,21.997175307694,15.425964150301,8.3087262441023,6.3480565201002,8.8575100984086,5.0097300937438,10.899056802057,13.898530555561,12.274479479612,19.262034919355,12.704951390281,16.60512218453,29.836798430671,11.787720640891,9.2089371408976,9.4885287092576,7.2104595679744,7.8762388661208,19.091432722399,19.075125715517,17.578865924359,18.734822481333,16.352095643637,4.4275630572268,16.218407341999,6.6252182312717,16.197114699429,18.226381716687,16.398094540103,0.40385638370952,18.424780602057,0.40052593299355,14.801651162531,7.2885585469282,14.851496994194,5.2812883188366,9.6224524959129,11.413270183692,10.269836063727,3.1054289539585,11.229618315583,1.904315570997,18.790728835961,2.2051187358729,20.45793004877,1.5154702996483,9.9590984656872,6.411048480504,9.3780411308426,7.7251876337595,17.355078777522,2.9477725246015,16.058958148107,2.1775126916243,13.495441006692,0.63444617856707,13.710411544669,19.073514273732,11.987598345817,12.522740119399,12.714561712883,13.771346576964,14.527503526623,15.481110912275,14.15206617202,13.585293679818,11.775463534839,3.8717322976241,10.734306208209,4.8958727594763,14.140403314808,4.0053235572412,13.294839577459,5.4871386626955,11.601551819105,7.1182108578403,14.841754251531,0.015467334964952,12.661314799025,2.0743564274027,14.414138324765,1.8894849415259,13.291412090197,11.806136545046,11.089181479155,10.754631116248,20.014056606141,0.14778712499941,14.305075087244,17.462993745175,13.347130078946,7.5990134344746,17.416700043619,1.4317843156503,14.902046026239,9.3182140611748,10.640726047943,8.6967980647733,12.428208554192,9.4288294683423,13.545283763669,10.375768060905}
Re: Code Doodles!
Voronoi Lines
my try to make voronoi regions in toroidal world, work in progress
my try to make voronoi regions in toroidal world, work in progress
Code: Select all
-- cc0, 2023, darkfrei
love.window.setTitle ('Voronoi Lines')
--love.window.setTitle ('Voronoi Lines on Toroidal World')
Points = {0,0,29.421497428895,18.262284985482,1.9791994775338,0.71104989006247,28.936347589706,1.3874202099944,0.77918559894233,1.7119154545849,0.74872496156755,18.75908887808,2.4294796576936,18.621404096665,0.74629667130324,17.272948180996,27.224705013952,0.60592345728928,28.113281538712,2.7081013486532,27.286359005096,4.183978333816,29.758882374693,3.3617643405968,26.731307726236,2.3346689791489,2.1391061763197,2.3662886471186,26.11672261621,18.802623146342,25.427344998163,0.35620449316817,28.21890597267,19.192023390212,3.7517173300831,1.7900008652362,1.981748592693,3.7754927204595,3.6491112562124,19.761768053947,4.674214819273,2.9490230330072,5.6408681601946,0.93025608865254,25.153278935918,3.5477422539136,24.74775908144,1.7408658927981,3.6468147671778,17.686777140792,2.7256975440463,16.586500273227,27.353484813482,17.711706794635,3.5807610961327,15.227472542947,1.447531381856,15.587596556207,4.8298954808599,16.620574836613,0.31465693871064,14.276126652375,29.511215767343,15.609803506531,2.1962809166391,13.945271943441,3.2672962025191,4.6199685102339,1.4474853623782,5.5148168661481,29.694158401428,5.2731956805743,5.37416143966,18.912707009383,26.946086120639,5.9227939397719,4.6932753886167,14.330123415792,6.9782426673136,18.413258136099,6.3445081320094,17.066505830656,8.2903487194649,19.086285734748,7.0252711656188,0.3873973193782,8.4495737245075,17.180138859889,24.698151332349,18.85177868671,26.012341279164,16.96074196664,28.960775498939,13.336494835331,0.90739685240295,12.873455631666,22.917225458181,19.363868669458,24.275265008088,17.465685829653,23.929765725272,0.54500552563436,6.7168409399952,2.1326746080956,9.098337121903,0.33492806859019,28.205328998772,14.904968278816,28.647034910054,16.852242409054,23.432729781527,4.2211867275551,23.574917883008,2.7937404923595,24.581165813541,5.5119634572548,0.68237096898904,7.1810494623508,2.2801117761457,7.2564079804841,3.1735159901111,6.11318099228,4.8951935725097,5.7195046128446,4.2766502409951,7.1216422599576,28.389755385136,6.248663557283,27.812609649106,7.7795543089628,25.39745634073,6.7148584441762,25.946492069892,4.8971573573492,26.243061820043,9.2012615989797,28.92375476746,8.9997849663966,6.6697296925175,13.811078483549,5.5586046248046,12.823025353141,3.9743078751966,12.610703282608,6.098882769853,15.109111748691,22.002857044804,2.6583741071336,22.509223849567,5.8222648100415,21.879813738722,4.1833119344917,24.406180342051,8.4994367798685,23.634969557267,7.3137831643219,27.373594538849,16.05002538136,23.448567626534,16.111926027443,22.366804890887,17.568268526608,24.958511918221,15.851389585741,9.8301356791575,18.572545833927,8.0128961369235,15.667830174963,10.113043830961,16.380111796853,9.11275904049,14.720274645358,7.5952044091645,12.660629455291,6.5275294429725,11.165210286208,4.5163089839191,11.071709542587,20.908047017226,5.938537862401,19.903335463998,4.4204289956641,20.608301362255,3.0710626746245,21.67628922207,0.090076372672286,22.760776162152,1.3969603245843,20.182403809908,7.284104666209,19.025181940175,6.0634745760051,21.782208297784,7.0547801776248,18.224622922412,7.8673727956669,17.378466772205,5.8153715963749,18.350488480217,4.1961035468554,21.757523856438,8.5664149354302,27.445342424373,10.133788761031,26.249118878489,10.922312935011,24.495579459621,9.9124655327756,11.594327665859,17.521496729008,11.71943630784,15.251330921105,16.448616033666,8.0877722215436,18.357595870684,9.81539689341,19.692768502809,8.9494817952152,29.527524694761,10.363938169432,0.28983215537493,8.5450233694436,25.945401964827,14.654425062408,17.101329771224,11.063643512431,19.927578543818,10.421911618694,18.851751125117,11.648940994635,16.611875213448,9.575802384804,9.5716853301274,1.6922636139561,8.0456709977104,1.6256254828659,10.933816062008,0.21014898415188,6.288352782026,3.6513728765422,1.6422720181125,9.9966177685061,8.1382361210998,10.737822980767,5.5290028087227,9.5324793548481,21.952304906453,10.723848774477,20.438720270184,12.123753127681,7.8337603688152,3.1691685127183,26.971947966862,13.660304229387,25.532421620072,12.959410658719,23.090454090237,9.3014071791645,17.562995943229,12.714553353205,19.046232707164,13.622709477516,7.1093722630043,5.5013454945225,9.5204935705948,13.318247355303,7.4576655393904,9.491729111497,3.6229295308856,9.0564833977333,5.7374655643323,7.8175367520628,3.1381991278438,10.393587001011,28.415378082348,11.64111327503,2.4157351681502,11.969362611613,17.740948964424,14.31840251801,19.804486852267,14.977275712126,20.834973898727,13.923332868907,18.488724639474,15.891118140154,20.429027062187,16.315774547216,16.154560697578,15.53719982925,15.763623513407,13.958621458879,15.142017683596,10.93762742525,15.843302664241,12.459627475133,22.477959557257,12.332835964072,20.228447124499,17.868882186928,23.354199318503,10.982858873003,24.357392061813,14.346981952072,18.533816474532,17.333983176357,17.231068059174,16.653718869598,22.545180836352,13.80445153977,24.219511279903,12.409347950979,21.997175307694,15.425964150301,8.3087262441023,6.3480565201002,8.8575100984086,5.0097300937438,10.899056802057,13.898530555561,12.274479479612,19.262034919355,12.704951390281,16.60512218453,29.836798430671,11.787720640891,9.2089371408976,9.4885287092576,7.2104595679744,7.8762388661208,19.091432722399,19.075125715517,17.578865924359,18.734822481333,16.352095643637,4.4275630572268,16.218407341999,6.6252182312717,16.197114699429,18.226381716687,16.398094540103,0.40385638370952,18.424780602057,0.40052593299355,14.801651162531,7.2885585469282,14.851496994194,5.2812883188366,9.6224524959129,11.413270183692,10.269836063727,3.1054289539585,11.229618315583,1.904315570997,18.790728835961,2.2051187358729,20.45793004877,1.5154702996483,9.9590984656872,6.411048480504,9.3780411308426,7.7251876337595,17.355078777522,2.9477725246015,16.058958148107,2.1775126916243,13.495441006692,0.63444617856707,13.710411544669,19.073514273732,11.987598345817,12.522740119399,12.714561712883,13.771346576964,14.527503526623,15.481110912275,14.15206617202,13.585293679818,11.775463534839,3.8717322976241,10.734306208209,4.8958727594763,14.140403314808,4.0053235572412,13.294839577459,5.4871386626955,11.601551819105,7.1182108578403,14.841754251531,0.015467334964952,12.661314799025,2.0743564274027,14.414138324765,1.8894849415259,13.291412090197,11.806136545046,11.089181479155,10.754631116248,20.014056606141,0.14778712499941,14.305075087244,17.462993745175,13.347130078946,7.5990134344746,17.416700043619,1.4317843156503,14.902046026239,9.3182140611748,10.640726047943,8.6967980647733,12.428208554192,9.4288294683423,13.545283763669,10.375768060905}
local function checkLineSegmentIntersection(segment, line)
local x1, y1, x2, y2 = segment[1], segment[2], segment[3], segment[4]
local x3, y3, x4, y4 = line[1], line[2], line[3], line[4]
local denominator = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1)
if denominator == 0 then
return false
end
local numerator1 = (x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)
local t1 = numerator1 / denominator
return t1 >= 0 and t1 < 1
end
local function calculateVoronoiRegions(points, gridWidth, gridHeight)
local regions = {}
for i = 1, #points - 3, 2 do
table.insert (regions, {x=points[i], y=points[i+1], lines={}})
end
-- Iterate over pairs of points
for i, regionA in ipairs (regions) do
local lines = regionA.lines
local p1x, p1y = regionA.x, regionA.y
for j, regionB in ipairs (regions) do
local p2x, p2y = regionB.x, regionB.y
if i == j then
-- same region, do nothing
else
-- Calculate the midpoint between the points
local midpointX = (p1x + p2x) / 2
local midpointY = (p1y + p2y) / 2
-- Calculate the direction vector from p1 to p2
local directionX = p2x - p1x
local directionY = p2y - p1y
-- Normalize the direction vector
local magnitude = math.sqrt(directionX^2 + directionY^2)
directionX = directionX / magnitude
directionY = directionY / magnitude
local lineLength = 0.35
-- Create the half-edge
local line = {
midpointX - directionY*lineLength,
midpointY + directionX*lineLength,
midpointX + directionY*lineLength,
midpointY - directionX*lineLength,
mx = midpointX,
my = midpointY,
}
table.insert(lines, line)
end
for k, lineA in ipairs (lines) do
local mx, my = lineA.mx, lineA.my
local segment = {p1x, p1y, mx, my} -- red line
for l, lineB in ipairs (lines) do -- yellow line
if not (lineA == lineB) then
if checkLineSegmentIntersection (segment, lineB) then
lineA.valid = false
end
end
end
end
for i = #lines, 1, -1 do
local line = lines[i]
if line.valid == false then
table.remove (lines, i)
end
end
local points = {}
for i = 1, #lines do
local j = (i)% #lines + 1
local lineA = lines[i]
local lineB = lines[j]
end
end
end
print ('#regions', #regions)
return regions
end
function love.load ()
GridWidth, GridHeight = 30, 20
Scale = math.floor (math.min (love.graphics.getWidth()/GridWidth, love.graphics.getHeight()/GridHeight))
local Width = GridWidth*Scale
local Height = GridHeight*Scale
love.window.setMode (Width, Height)
Regions = calculateVoronoiRegions(Points, GridWidth, GridHeight)
end
function love.draw()
love.graphics.scale (Scale)
love.graphics.setPointSize (3)
love.graphics.setLineWidth (0.25/Scale)
love.graphics.setColor (0.5,0.5,0.5)
for i, region in ipairs (Regions) do
for j, line in ipairs (region.lines) do
love.graphics.line (line)
end
end
local index = 204
local region = Regions[index]
local x, y = region.x, region.y
for j, line in ipairs (region.lines) do
love.graphics.setColor (1,0,0)
love.graphics.line ({x, y, line.mx, line.my})
love.graphics.setColor (1,1,0)
love.graphics.line (line)
love.graphics.points (line.mx, line.my)
end
love.graphics.setColor (1,1,1)
for i, region in ipairs (Regions) do
love.graphics.points (region.x, region.y)
end
love.graphics.scale (1/Scale)
love.graphics.setColor (1,1,1)
for i, region in ipairs (Regions) do
love.graphics.print (i, region.x*Scale-10, region.y*Scale+1)
end
end
Re: Code Doodles!
It looks better, but the filter looks not right:
Code: Select all
-- License CC0 (Creative Commons license) (c) darkfrei, 2023
love.window.setMode(1024, 1024)
Width, Height = love.graphics.getDimensions( )
-- cc0, 2023, darkfrei
love.window.setTitle ('Voronoi Lines')
--love.window.setTitle ('Voronoi Lines on Toroidal World')
Points = {0,0,29.421497428895,18.262284985482,1.9791994775338,0.71104989006247,28.936347589706,1.3874202099944,0.77918559894233,1.7119154545849,0.74872496156755,18.75908887808,2.4294796576936,18.621404096665,0.74629667130324,17.272948180996,27.224705013952,0.60592345728928,28.113281538712,2.7081013486532,27.286359005096,4.183978333816,29.758882374693,3.3617643405968,26.731307726236,2.3346689791489,2.1391061763197,2.3662886471186,26.11672261621,18.802623146342,25.427344998163,0.35620449316817,28.21890597267,19.192023390212,3.7517173300831,1.7900008652362,1.981748592693,3.7754927204595,3.6491112562124,19.761768053947,4.674214819273,2.9490230330072,5.6408681601946,0.93025608865254,25.153278935918,3.5477422539136,24.74775908144,1.7408658927981,3.6468147671778,17.686777140792,2.7256975440463,16.586500273227,27.353484813482,17.711706794635,3.5807610961327,15.227472542947,1.447531381856,15.587596556207,4.8298954808599,16.620574836613,0.31465693871064,14.276126652375,29.511215767343,15.609803506531,2.1962809166391,13.945271943441,3.2672962025191,4.6199685102339,1.4474853623782,5.5148168661481,29.694158401428,5.2731956805743,5.37416143966,18.912707009383,26.946086120639,5.9227939397719,4.6932753886167,14.330123415792,6.9782426673136,18.413258136099,6.3445081320094,17.066505830656,8.2903487194649,19.086285734748,7.0252711656188,0.3873973193782,8.4495737245075,17.180138859889,24.698151332349,18.85177868671,26.012341279164,16.96074196664,28.960775498939,13.336494835331,0.90739685240295,12.873455631666,22.917225458181,19.363868669458,24.275265008088,17.465685829653,23.929765725272,0.54500552563436,6.7168409399952,2.1326746080956,9.098337121903,0.33492806859019,28.205328998772,14.904968278816,28.647034910054,16.852242409054,23.432729781527,4.2211867275551,23.574917883008,2.7937404923595,24.581165813541,5.5119634572548,0.68237096898904,7.1810494623508,2.2801117761457,7.2564079804841,3.1735159901111,6.11318099228,4.8951935725097,5.7195046128446,4.2766502409951,7.1216422599576,28.389755385136,6.248663557283,27.812609649106,7.7795543089628,25.39745634073,6.7148584441762,25.946492069892,4.8971573573492,26.243061820043,9.2012615989797,28.92375476746,8.9997849663966,6.6697296925175,13.811078483549,5.5586046248046,12.823025353141,3.9743078751966,12.610703282608,6.098882769853,15.109111748691,22.002857044804,2.6583741071336,22.509223849567,5.8222648100415,21.879813738722,4.1833119344917,24.406180342051,8.4994367798685,23.634969557267,7.3137831643219,27.373594538849,16.05002538136,23.448567626534,16.111926027443,22.366804890887,17.568268526608,24.958511918221,15.851389585741,9.8301356791575,18.572545833927,8.0128961369235,15.667830174963,10.113043830961,16.380111796853,9.11275904049,14.720274645358,7.5952044091645,12.660629455291,6.5275294429725,11.165210286208,4.5163089839191,11.071709542587,20.908047017226,5.938537862401,19.903335463998,4.4204289956641,20.608301362255,3.0710626746245,21.67628922207,0.090076372672286,22.760776162152,1.3969603245843,20.182403809908,7.284104666209,19.025181940175,6.0634745760051,21.782208297784,7.0547801776248,18.224622922412,7.8673727956669,17.378466772205,5.8153715963749,18.350488480217,4.1961035468554,21.757523856438,8.5664149354302,27.445342424373,10.133788761031,26.249118878489,10.922312935011,24.495579459621,9.9124655327756,11.594327665859,17.521496729008,11.71943630784,15.251330921105,16.448616033666,8.0877722215436,18.357595870684,9.81539689341,19.692768502809,8.9494817952152,29.527524694761,10.363938169432,0.28983215537493,8.5450233694436,25.945401964827,14.654425062408,17.101329771224,11.063643512431,19.927578543818,10.421911618694,18.851751125117,11.648940994635,16.611875213448,9.575802384804,9.5716853301274,1.6922636139561,8.0456709977104,1.6256254828659,10.933816062008,0.21014898415188,6.288352782026,3.6513728765422,1.6422720181125,9.9966177685061,8.1382361210998,10.737822980767,5.5290028087227,9.5324793548481,21.952304906453,10.723848774477,20.438720270184,12.123753127681,7.8337603688152,3.1691685127183,26.971947966862,13.660304229387,25.532421620072,12.959410658719,23.090454090237,9.3014071791645,17.562995943229,12.714553353205,19.046232707164,13.622709477516,7.1093722630043,5.5013454945225,9.5204935705948,13.318247355303,7.4576655393904,9.491729111497,3.6229295308856,9.0564833977333,5.7374655643323,7.8175367520628,3.1381991278438,10.393587001011,28.415378082348,11.64111327503,2.4157351681502,11.969362611613,17.740948964424,14.31840251801,19.804486852267,14.977275712126,20.834973898727,13.923332868907,18.488724639474,15.891118140154,20.429027062187,16.315774547216,16.154560697578,15.53719982925,15.763623513407,13.958621458879,15.142017683596,10.93762742525,15.843302664241,12.459627475133,22.477959557257,12.332835964072,20.228447124499,17.868882186928,23.354199318503,10.982858873003,24.357392061813,14.346981952072,18.533816474532,17.333983176357,17.231068059174,16.653718869598,22.545180836352,13.80445153977,24.219511279903,12.409347950979,21.997175307694,15.425964150301,8.3087262441023,6.3480565201002,8.8575100984086,5.0097300937438,10.899056802057,13.898530555561,12.274479479612,19.262034919355,12.704951390281,16.60512218453,29.836798430671,11.787720640891,9.2089371408976,9.4885287092576,7.2104595679744,7.8762388661208,19.091432722399,19.075125715517,17.578865924359,18.734822481333,16.352095643637,4.4275630572268,16.218407341999,6.6252182312717,16.197114699429,18.226381716687,16.398094540103,0.40385638370952,18.424780602057,0.40052593299355,14.801651162531,7.2885585469282,14.851496994194,5.2812883188366,9.6224524959129,11.413270183692,10.269836063727,3.1054289539585,11.229618315583,1.904315570997,18.790728835961,2.2051187358729,20.45793004877,1.5154702996483,9.9590984656872,6.411048480504,9.3780411308426,7.7251876337595,17.355078777522,2.9477725246015,16.058958148107,2.1775126916243,13.495441006692,0.63444617856707,13.710411544669,19.073514273732,11.987598345817,12.522740119399,12.714561712883,13.771346576964,14.527503526623,15.481110912275,14.15206617202,13.585293679818,11.775463534839,3.8717322976241,10.734306208209,4.8958727594763,14.140403314808,4.0053235572412,13.294839577459,5.4871386626955,11.601551819105,7.1182108578403,14.841754251531,0.015467334964952,12.661314799025,2.0743564274027,14.414138324765,1.8894849415259,13.291412090197,11.806136545046,11.089181479155,10.754631116248,20.014056606141,0.14778712499941,14.305075087244,17.462993745175,13.347130078946,7.5990134344746,17.416700043619,1.4317843156503,14.902046026239,9.3182140611748,10.640726047943,8.6967980647733,12.428208554192,9.4288294683423,13.545283763669,10.375768060905}
local function findRaysIntersection (x1,y1,x2,y2, x3,y3,x4,y4)
local denominator = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4)
if denominator == 0 then
return nil
end
local dx1, dy1 = (x1 - x3), (y1 - y3)
local t = (dx1 * (y3 - y4) - dy1 * (x3 - x4)) / denominator
local u = (dx1 * (y1 - y2) - dy1 * (x1 - x2)) / denominator
if t >= 0 and u >= 0 then
local dx = t * (x2 - x1)
local dy = t * (y2 - y1)
local intersectionX = x1 + dx
local intersectionY = y1 + dy
local t1 = math.sqrt(dx*dx+dy*dy)
local t2 = math.sqrt((intersectionX-x4)^2+(intersectionY-y4)^2)
return intersectionX, intersectionY, t1, t2
end
return nil
end
local function checkLineSegmentIntersection(segment, line)
local x1, y1, x2, y2 = segment[1], segment[2], segment[3], segment[4]
local x3, y3, x4, y4 = line[1], line[2], line[3], line[4]
local denominator = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1)
if denominator == 0 then
return false
end
local numerator1 = (x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)
local t = numerator1 / denominator
if t >= 0 and t < 1 then
local intersectionX = x1 + t * (x2 - x1)
local intersectionY = y1 + t * (y2 - y1)
return intersectionX, intersectionY
end
end
local function calculateVoronoiRegions(points, gridWidth, gridHeight)
local regions = {}
for i = 1, #points - 3, 2 do
table.insert (regions, {
x=points[i],
y=points[i+1],
lines={},
points={},
})
end
-- Iterate over pairs of points
for i, regionA in ipairs (regions) do
local lines = regionA.lines
local points = regionA.points
local p1x, p1y = regionA.x, regionA.y
for j, regionB in ipairs (regions) do
local p2x, p2y = regionB.x, regionB.y
if i == j then
-- same region, do nothing
else
-- Calculate the midpoint between the points
local midpointX = (p1x + p2x) / 2
local midpointY = (p1y + p2y) / 2
-- Calculate the direction vector from p1 to p2
local directionX = p2x - p1x
local directionY = p2y - p1y
-- Normalize the direction vector
local magnitude = math.sqrt(directionX^2 + directionY^2)
directionX = directionX / magnitude
directionY = directionY / magnitude
local lineLength = 1.41
-- Create the half-edge
local line = {
midpointX - directionY*lineLength,
midpointY + directionX*lineLength,
midpointX + directionY*lineLength,
midpointY - directionX*lineLength,
mx = midpointX,
my = midpointY,
t1 = lineLength,
t2 = lineLength,
angle = math.atan2 (midpointY-p1y, midpointX-p1x),
}
table.insert(lines, line)
end
end
table.sort (lines, function (a, b) return a.angle < b.angle end)
for k, lineA in ipairs (lines) do
local mx, my = lineA.mx, lineA.my
local segment = {p1x, p1y, mx, my} -- red line
for l, lineB in ipairs (lines) do -- yellow line
if not (lineA == lineB) then
if checkLineSegmentIntersection (segment, lineB) then
lineA.valid = false
end
end
end
end
for i = #lines, 1, -1 do
local line = lines[i]
if line.valid == false then
table.remove (lines, i)
end
end
-- table.sort (lines, function (a, b) return a.angle < b.angle end)
-- all regions B are done
for i = 1, #lines do
local j = (i)% #lines + 1
local lineA = lines[i]
local lineB = lines[j]
local xa,ya = lineA.mx, lineA.my
local xb,yb = lineB.mx, lineB.my
local xa1,ya1, xa2,ya2 = lineA[1], lineA[2], lineA[3], lineA[4]
local xb1,yb1, xb2,yb2 = lineB[1], lineB[2], lineB[3], lineB[4]
local tx1, ty1, t11, t12 = findRaysIntersection (xa,ya,xa2,ya2, xb,yb,xb1,yb1)
if tx1 and (t11 < lineA.t2) and (t12 < lineB.t1) then
print ('tx1')
lineA[3], lineA[4] = tx1, ty1
lineB[1], lineB[2] = tx1, ty1
lineA.t2 = t11
lineB.t1 = t12
end
local tx2, ty2, t21, t22 = findRaysIntersection (xa,ya,xa1,ya1, xb,yb,xb2,yb2)
if tx2 and (t21 < lineA.t1) and (t22 < lineB.t2) then
-- print ('tx2')
lineA[1], lineA[2] = tx2, ty2
lineB[3], lineB[4] = tx2, ty2
lineA.t1 = t21
lineB.t2 = t22
end
end
end
print ('#regions', #regions)
return regions
end
function love.load ()
GridWidth, GridHeight = 30, 20
Scale = math.floor (math.min (love.graphics.getWidth()/GridWidth, love.graphics.getHeight()/GridHeight))
local Width = GridWidth*Scale
local Height = GridHeight*Scale
love.window.setMode (Width, Height)
Regions = calculateVoronoiRegions(Points, GridWidth, GridHeight)
end
function love.draw()
love.graphics.scale (Scale)
love.graphics.setPointSize (5)
love.graphics.setLineWidth (0.25/Scale)
love.graphics.setColor (0.5,0.5,0.5)
for i, region in ipairs (Regions) do
for j, line in ipairs (region.lines) do
love.graphics.line (line)
end
-- if region.points then
-- love.graphics.points (region.points)
-- end
end
local index = 204
local region = Regions[index]
local x, y = region.x, region.y
for j, line in ipairs (region.lines) do
love.graphics.setColor (1,0,0)
love.graphics.line ({x, y, line.mx, line.my})
love.graphics.setColor (1,1,0)
love.graphics.line (line)
love.graphics.points (line.mx, line.my)
end
if region.points then
love.graphics.setColor (0,1,0)
love.graphics.points (region.points)
end
love.graphics.setColor (1,1,1)
for i, region in ipairs (Regions) do
love.graphics.points (region.x, region.y)
end
love.graphics.scale (1/Scale)
love.graphics.setColor (1,1,1)
for i, region in ipairs (Regions) do
love.graphics.print (i, region.x*Scale-10, region.y*Scale+1)
end
end
function love.keypressed(key, scancode, isrepeat)
if false then
elseif key == "escape" then
love.event.quit()
end
end
Code: Select all
-- License CC0 (Creative Commons license) (c) darkfrei, 2023
love.window.setMode(1200, 800)
Width, Height = love.graphics.getDimensions( )
love.window.setTitle ('Voronoi Lines')
--love.window.setTitle ('Voronoi Lines on Toroidal World')
Points = {0,0,29.421497428895,18.262284985482,1.9791994775338,0.71104989006247,28.936347589706,1.3874202099944,0.77918559894233,1.7119154545849,0.74872496156755,18.75908887808,2.4294796576936,18.621404096665,0.74629667130324,17.272948180996,27.224705013952,0.60592345728928,28.113281538712,2.7081013486532,27.286359005096,4.183978333816,29.758882374693,3.3617643405968,26.731307726236,2.3346689791489,2.1391061763197,2.3662886471186,26.11672261621,18.802623146342,25.427344998163,0.35620449316817,28.21890597267,19.192023390212,3.7517173300831,1.7900008652362,1.981748592693,3.7754927204595,3.6491112562124,19.761768053947,4.674214819273,2.9490230330072,5.6408681601946,0.93025608865254,25.153278935918,3.5477422539136,24.74775908144,1.7408658927981,3.6468147671778,17.686777140792,2.7256975440463,16.586500273227,27.353484813482,17.711706794635,3.5807610961327,15.227472542947,1.447531381856,15.587596556207,4.8298954808599,16.620574836613,0.31465693871064,14.276126652375,29.511215767343,15.609803506531,2.1962809166391,13.945271943441,3.2672962025191,4.6199685102339,1.4474853623782,5.5148168661481,29.694158401428,5.2731956805743,5.37416143966,18.912707009383,26.946086120639,5.9227939397719,4.6932753886167,14.330123415792,6.9782426673136,18.413258136099,6.3445081320094,17.066505830656,8.2903487194649,19.086285734748,7.0252711656188,0.3873973193782,8.4495737245075,17.180138859889,24.698151332349,18.85177868671,26.012341279164,16.96074196664,28.960775498939,13.336494835331,0.90739685240295,12.873455631666,22.917225458181,19.363868669458,24.275265008088,17.465685829653,23.929765725272,0.54500552563436,6.7168409399952,2.1326746080956,9.098337121903,0.33492806859019,28.205328998772,14.904968278816,28.647034910054,16.852242409054,23.432729781527,4.2211867275551,23.574917883008,2.7937404923595,24.581165813541,5.5119634572548,0.68237096898904,7.1810494623508,2.2801117761457,7.2564079804841,3.1735159901111,6.11318099228,4.8951935725097,5.7195046128446,4.2766502409951,7.1216422599576,28.389755385136,6.248663557283,27.812609649106,7.7795543089628,25.39745634073,6.7148584441762,25.946492069892,4.8971573573492,26.243061820043,9.2012615989797,28.92375476746,8.9997849663966,6.6697296925175,13.811078483549,5.5586046248046,12.823025353141,3.9743078751966,12.610703282608,6.098882769853,15.109111748691,22.002857044804,2.6583741071336,22.509223849567,5.8222648100415,21.879813738722,4.1833119344917,24.406180342051,8.4994367798685,23.634969557267,7.3137831643219,27.373594538849,16.05002538136,23.448567626534,16.111926027443,22.366804890887,17.568268526608,24.958511918221,15.851389585741,9.8301356791575,18.572545833927,8.0128961369235,15.667830174963,10.113043830961,16.380111796853,9.11275904049,14.720274645358,7.5952044091645,12.660629455291,6.5275294429725,11.165210286208,4.5163089839191,11.071709542587,20.908047017226,5.938537862401,19.903335463998,4.4204289956641,20.608301362255,3.0710626746245,21.67628922207,0.090076372672286,22.760776162152,1.3969603245843,20.182403809908,7.284104666209,19.025181940175,6.0634745760051,21.782208297784,7.0547801776248,18.224622922412,7.8673727956669,17.378466772205,5.8153715963749,18.350488480217,4.1961035468554,21.757523856438,8.5664149354302,27.445342424373,10.133788761031,26.249118878489,10.922312935011,24.495579459621,9.9124655327756,11.594327665859,17.521496729008,11.71943630784,15.251330921105,16.448616033666,8.0877722215436,18.357595870684,9.81539689341,19.692768502809,8.9494817952152,29.527524694761,10.363938169432,0.28983215537493,8.5450233694436,25.945401964827,14.654425062408,17.101329771224,11.063643512431,19.927578543818,10.421911618694,18.851751125117,11.648940994635,16.611875213448,9.575802384804,9.5716853301274,1.6922636139561,8.0456709977104,1.6256254828659,10.933816062008,0.21014898415188,6.288352782026,3.6513728765422,1.6422720181125,9.9966177685061,8.1382361210998,10.737822980767,5.5290028087227,9.5324793548481,21.952304906453,10.723848774477,20.438720270184,12.123753127681,7.8337603688152,3.1691685127183,26.971947966862,13.660304229387,25.532421620072,12.959410658719,23.090454090237,9.3014071791645,17.562995943229,12.714553353205,19.046232707164,13.622709477516,7.1093722630043,5.5013454945225,9.5204935705948,13.318247355303,7.4576655393904,9.491729111497,3.6229295308856,9.0564833977333,5.7374655643323,7.8175367520628,3.1381991278438,10.393587001011,28.415378082348,11.64111327503,2.4157351681502,11.969362611613,17.740948964424,14.31840251801,19.804486852267,14.977275712126,20.834973898727,13.923332868907,18.488724639474,15.891118140154,20.429027062187,16.315774547216,16.154560697578,15.53719982925,15.763623513407,13.958621458879,15.142017683596,10.93762742525,15.843302664241,12.459627475133,22.477959557257,12.332835964072,20.228447124499,17.868882186928,23.354199318503,10.982858873003,24.357392061813,14.346981952072,18.533816474532,17.333983176357,17.231068059174,16.653718869598,22.545180836352,13.80445153977,24.219511279903,12.409347950979,21.997175307694,15.425964150301,8.3087262441023,6.3480565201002,8.8575100984086,5.0097300937438,10.899056802057,13.898530555561,12.274479479612,19.262034919355,12.704951390281,16.60512218453,29.836798430671,11.787720640891,9.2089371408976,9.4885287092576,7.2104595679744,7.8762388661208,19.091432722399,19.075125715517,17.578865924359,18.734822481333,16.352095643637,4.4275630572268,16.218407341999,6.6252182312717,16.197114699429,18.226381716687,16.398094540103,0.40385638370952,18.424780602057,0.40052593299355,14.801651162531,7.2885585469282,14.851496994194,5.2812883188366,9.6224524959129,11.413270183692,10.269836063727,3.1054289539585,11.229618315583,1.904315570997,18.790728835961,2.2051187358729,20.45793004877,1.5154702996483,9.9590984656872,6.411048480504,9.3780411308426,7.7251876337595,17.355078777522,2.9477725246015,16.058958148107,2.1775126916243,13.495441006692,0.63444617856707,13.710411544669,19.073514273732,11.987598345817,12.522740119399,12.714561712883,13.771346576964,14.527503526623,15.481110912275,14.15206617202,13.585293679818,11.775463534839,3.8717322976241,10.734306208209,4.8958727594763,14.140403314808,4.0053235572412,13.294839577459,5.4871386626955,11.601551819105,7.1182108578403,14.841754251531,0.015467334964952,12.661314799025,2.0743564274027,14.414138324765,1.8894849415259,13.291412090197,11.806136545046,11.089181479155,10.754631116248,20.014056606141,0.14778712499941,14.305075087244,17.462993745175,13.347130078946,7.5990134344746,17.416700043619,1.4317843156503,14.902046026239,9.3182140611748,10.640726047943,8.6967980647733,12.428208554192,9.4288294683423,13.545283763669,10.375768060905}
local function findRaysIntersection (x1,y1,x2,y2, x3,y3,x4,y4)
local denominator = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4)
if denominator == 0 then
return nil
end
local dx1, dy1 = (x1 - x3), (y1 - y3)
local t = (dx1 * (y3 - y4) - dy1 * (x3 - x4)) / denominator
local u = (dx1 * (y1 - y2) - dy1 * (x1 - x2)) / denominator
if t >= 0 and u >= 0 then
local dx = t * (x2 - x1)
local dy = t * (y2 - y1)
local intersectionX = x1 + dx
local intersectionY = y1 + dy
local t1 = math.sqrt(dx*dx+dy*dy)
local t2 = math.sqrt((intersectionX-x4)^2+(intersectionY-y4)^2)
return intersectionX, intersectionY, t1, t2
end
return nil
end
local function checkLineSegmentIntersection(segment, line)
local x1, y1, x2, y2 = segment[1], segment[2], segment[3], segment[4]
local x3, y3, x4, y4 = line[1], line[2], line[3], line[4]
local denominator = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1)
if denominator == 0 then
return false
end
local numerator1 = (x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)
local t = numerator1 / denominator
if t >= 0 and t < 1 then
local intersectionX = x1 + t * (x2 - x1)
local intersectionY = y1 + t * (y2 - y1)
return intersectionX, intersectionY
end
end
local function calculateVoronoiRegions(points, gridWidth, gridHeight)
local regions = {}
for i = 1, #points - 3, 2 do
table.insert (regions, {
x=points[i],
y=points[i+1],
lines={},
points={},
})
end
-- Iterate over pairs of points
for i, regionA in ipairs (regions) do
local lines = regionA.lines
local points = regionA.points
local p1x, p1y = regionA.x, regionA.y
for j, regionB in ipairs (regions) do
local p2x, p2y = regionB.x, regionB.y
local dx = p2x-p1x
local dy = p2y-p1y
if dx > gridWidth / 2 then
dx = dx - gridWidth
elseif dx < - gridWidth / 2 then
dx = gridWidth + dx
end
if dy > gridHeight / 2 then
dy = dy - gridHeight
elseif dy < - gridHeight / 2 then
dy = gridHeight + dy
end
p2x = p1x + dx
p2y = p1y + dy
if i == j then
-- same region, do nothing
else
-- Calculate the midpoint between the points
local midpointX = (p1x + p2x) / 2
local midpointY = (p1y + p2y) / 2
-- Calculate the direction vector from p1 to p2
local directionX = p2x - p1x
local directionY = p2y - p1y
-- Normalize the direction vector
local magnitude = math.sqrt(directionX^2 + directionY^2)
directionX = directionX / magnitude
directionY = directionY / magnitude
local lineLength = 10
-- Create the half-edge
local line = {
midpointX - directionY*lineLength,
midpointY + directionX*lineLength,
midpointX + directionY*lineLength,
midpointY - directionX*lineLength,
mx = midpointX,
my = midpointY,
t1 = lineLength,
t2 = lineLength,
angle = math.atan2 (midpointY-p1y, midpointX-p1x),
}
table.insert(lines, line)
end
end
table.sort (lines, function (a, b) return a.angle < b.angle end)
for k, lineA in ipairs (lines) do
local mx, my = lineA.mx, lineA.my
local segment = {p1x, p1y, mx, my} -- red line
for l, lineB in ipairs (lines) do -- yellow line
if not (lineA == lineB) then
if checkLineSegmentIntersection (segment, lineB) then
lineA.valid = false
end
end
end
end
for i = #lines, 1, -1 do
local line = lines[i]
if line.valid == false then
table.remove (lines, i)
end
end
-- table.sort (lines, function (a, b) return a.angle < b.angle end)
-- all regions B are done
for iLine = 1, #lines do
local jLine = (iLine)% #lines + 1
local lineA = lines[iLine]
local lineB = lines[jLine]
local xa,ya = lineA.mx, lineA.my
local xb,yb = lineB.mx, lineB.my
local xa1,ya1, xa2,ya2 = lineA[1], lineA[2], lineA[3], lineA[4]
local xb1,yb1, xb2,yb2 = lineB[1], lineB[2], lineB[3], lineB[4]
local tx2, ty2, t21, t22 = findRaysIntersection (xa,ya,xa1,ya1, xb,yb,xb2,yb2)
if tx2 and (t21 < lineA.t1) and (t22 < lineB.t2) then
lineA[1], lineA[2] = tx2, ty2
lineB[3], lineB[4] = tx2, ty2
lineA.t1 = t21
lineB.t2 = t22
end
end
end
print ('#regions', #regions)
return regions
end
function love.load ()
GridWidth, GridHeight = 30, 20
Scale = math.floor (math.min (love.graphics.getWidth()/GridWidth, love.graphics.getHeight()/GridHeight))
Scale = 32
local Width = GridWidth*Scale
local Height = GridHeight*Scale
-- love.window.setMode (Width, Height)
Regions = calculateVoronoiRegions(Points, GridWidth, GridHeight)
end
function love.draw()
love.graphics.scale (Scale)
love.graphics.translate (3,2)
love.graphics.setPointSize (5)
love.graphics.setLineWidth (0.25/Scale)
love.graphics.setColor (0.5,0.5,0.5)
for i, region in ipairs (Regions) do
for j, line in ipairs (region.lines) do
love.graphics.line (line)
end
end
local index = 204
local region = Regions[index]
local x, y = region.x, region.y
for j, line in ipairs (region.lines) do
love.graphics.setColor (1,0,0)
love.graphics.line ({x, y, line.mx, line.my})
love.graphics.setColor (1,1,0)
love.graphics.line (line)
love.graphics.points (line.mx, line.my)
end
if region.points then
love.graphics.setColor (0,1,0)
love.graphics.points (region.points)
end
love.graphics.setColor (1,1,1)
for i, region in ipairs (Regions) do
love.graphics.points (region.x, region.y)
end
love.graphics.scale (1/Scale)
love.graphics.setColor (1,1,1)
for i, region in ipairs (Regions) do
love.graphics.print (i, region.x*Scale-10, region.y*Scale+1)
end
end
function love.keypressed(key, scancode, isrepeat)
if false then
elseif key == "escape" then
love.event.quit()
end
end
Re: Code Doodles!
Polygon cutter: how to cut the part of polygon with an infinite line (with direction).
A lot of optimizations is possible
A lot of optimizations is possible
Code: Select all
local function findIntersection(x1, y1, x2, y2, x3, y3, x4, y4)
-- x1, y1, x2, y2 - segment
-- x3, y3, x4, y4 - continuous line
local dx1, dy1 = x2-x1, y2-y1
local dx2, dy2 = x4-x3, y4-y3
local denominator = dx1*dy2-dy1*dx2
if denominator == 0 then
return nil
end
local dx13, dy13 = (x1 - x3), (y1 - y3)
local t = (dx13 * (y3 - y4) - dy13 * (x3 - x4)) / denominator
local u = (dx13 * (y1 - y2) - dy13 * (x1 - x2)) / denominator
if t >= 0 and t <= 1 and u >= 0 then
local intersectionX = x1 + t * (x2 - x1)
local intersectionY = y1 + t * (y2 - y1)
return intersectionX, intersectionY
end
return nil
end
local function pseudoScalarProduct(x1, y1, x2, y2, x3, y3, x4, y4)
local dx1, dy1 = x2-x1, y2-y1
local dx2, dy2 = x4-x3, y4-y3
-- positive - goes inside, clockwise
return dx1 * dy2 - dx2 * dy1
end
local function cropPolygon(polygon, line)
local x3, y3, x4, y4 = line[1], line[2], line[3], line[4]
local segments = {}
local goesInside = nil
for i = 1, #polygon-1, 2 do
local x1, y1 = polygon[i], polygon[i + 1]
local j = (i + 1)%(#polygon)+1
local x2, y2 = polygon[j], polygon[j + 1]
local cx, cy = findIntersection(x1, y1, x2, y2, x3, y3, x4, y4)
local segment = {x1=x1, y1=y1, x2=x2, y2=y2}
if cx then
segment.cx = cx
segment.cy = cy
goesInside = pseudoScalarProduct(x1, y1, x2, y2, x3, y3, x4, y4) > 0
segment.goesInside = goesInside
elseif goesInside ~= nil then
segment.goesInside = goesInside
end
table.insert(segments, segment)
end
for i = 1, #segments do
local segment = segments[i]
if segment.goesInside == nil then
segment.goesInside = goesInside
else
break
end
end
local clippedPolygon = {}
-- print (' ', #segments)
local isInside
local firstSegment = segments[1]
if not firstSegment.cx and not firstSegment.goesInside then
table.insert (clippedPolygon, firstSegment.x1)
table.insert (clippedPolygon, firstSegment.y1)
elseif firstSegment.cx and firstSegment.goesInside then
table.insert (clippedPolygon, firstSegment.x1)
table.insert (clippedPolygon, firstSegment.y1)
end
local cx, cy
for i = 1, #segments do
local segment = segments[i]
if segment.cx and segment.goesInside then
-- print (i, 'cx, ins')
table.insert (clippedPolygon, segment.cx)
table.insert (clippedPolygon, segment.cy)
table.insert (clippedPolygon, cx)
table.insert (clippedPolygon, cy)
elseif segment.goesInside then
--print (i, 'ins')
-- do nothing
elseif segment.cx and not segment.goesInside then
--print (i, 'cx, not ins')
table.insert (clippedPolygon, segment.cx)
table.insert (clippedPolygon, segment.cy)
if (not (i == #segments)) then
table.insert (clippedPolygon, segment.x2)
table.insert (clippedPolygon, segment.y2)
end
-- for not polygons:
-- cx, cy = segment.cx, segment.cy
elseif not segment.goesInside then
--print (i, 'not ins')
if (not (i == #segments)) then
table.insert (clippedPolygon, segment.x2)
table.insert (clippedPolygon, segment.y2)
end
else
--print ('what')
end
end
return clippedPolygon
end
polygon1 = {300, 100, 600, 200, 500, 400, 200, 500}
--polygon1 = {300, 100, 800, 200, 900, 400}
cropperLine = {100,100, 600, 50}
polygon2 = cropPolygon (polygon1, cropperLine)
function love.load()
end
function love.update(dt)
end
function love.draw()
love.graphics.setLineWidth (3)
love.graphics.setColor (0.5,0.5,0.5)
love.graphics.polygon ('line', polygon1)
-- love.graphics.line (polygon1)
love.graphics.setColor (0.8,0,0)
love.graphics.line (cropperLine)
if polygon2 and #polygon2 > 2 then
love.graphics.setColor (0,0.8,0)
love.graphics.polygon ('line', polygon2)
-- love.graphics.line (polygon2)
end
end
function love.mousepressed( x, y, button, istouch, presses )
cropperLine[1] = x
cropperLine[2] = y
polygon2 = cropPolygon (polygon1, cropperLine)
end
function love.mousemoved( x, y, dx, dy, istouch )
cropperLine[3] = x
cropperLine[4] = y
polygon2 = cropPolygon (polygon1, cropperLine)
end
Who is online
Users browsing this forum: Google [Bot] and 7 guests