Skip to main content
  • Home
  • Development
  • Documentation
  • Donate
  • Operational login
  • Browse the archive

swh logo
SoftwareHeritage
Software
Heritage
Archive
Features
  • Search

  • Downloads

  • Save code now

  • Add forge now

  • Help

https://github.com/nithin1527/onion-soup
03 June 2025, 15:20:27 UTC
  • Code
  • Branches (1)
  • Releases (0)
  • Visits
    • Branches
    • Releases
    • HEAD
    • refs/heads/main
    • 698a000737b45477597e7ffc8f17e59072bfe7f3
    No releases to show
  • 8d9d496
  • /
  • onion.lua
Raw File Download Save again
Take a new snapshot of a software origin

If the archived software origin currently browsed is not synchronized with its upstream version (for instance when new commits have been issued), you can explicitly request Software Heritage to take a new snapshot of it.

Use the form below to proceed. Once a request has been submitted and accepted, it will be processed as soon as possible. You can then check its processing state by visiting this dedicated page.
swh spinner

Processing "take a new snapshot" request ...

To reference or cite the objects present in the Software Heritage archive, permalinks based on SoftWare Hash IDentifiers (SWHIDs) must be used.
Select below a type of object currently browsed in order to display its associated SWHID and permalink.

  • content
  • directory
  • revision
  • snapshot
origin badgecontent badge
swh:1:cnt:993638503b91b6463e6fb99e489cdb3c377047e5
origin badgedirectory badge
swh:1:dir:8d9d49631e2688049de55f2c874e85e56cb50620
origin badgerevision badge
swh:1:rev:698a000737b45477597e7ffc8f17e59072bfe7f3
origin badgesnapshot badge
swh:1:snp:c055d63f5a6fe8fb1d9092a820512b5f1eea62fa

This interface enables to generate software citations, provided that the root directory of browsed objects contains a citation.cff or codemeta.json file.
Select below a type of object currently browsed in order to generate citations for them.

  • content
  • directory
  • revision
  • snapshot
(requires biblatex-software package)
Generating citation ...
(requires biblatex-software package)
Generating citation ...
(requires biblatex-software package)
Generating citation ...
(requires biblatex-software package)
Generating citation ...
Tip revision: 698a000737b45477597e7ffc8f17e59072bfe7f3 authored by nithin1527 on 12 April 2025, 02:19:45 UTC
Update README.md
Tip revision: 698a000
onion.lua
label = "Points and Polygons"
methods = {{ label = "Onion Peeling", run = runOnionPeeling }}
revertOriginal = _G.revertOriginal
about = [[
    This ipelet creates an onion peeling of the selected points.
    It is based on the convex hull algorithm.
]]

function incorrect(title, model) model:warning(title) end

-- ========================================================================================================================
--! CONVEX HULL (GRAHAM SCAN)
-- https://www.codingdrills.com/tutorial/introduction-to-divide-and-conquer-algorithms/convex-hull-graham-scan

-- Function to calculate the squared distance between two points
function squared_distance(p1, p2)
    return (p1.x - p2.x)^2 + (p1.y - p2.y)^2
end

-- Function to compare two points with respect to a given 'lowest' point
-- Closure over the lowest point to create a compare function
function create_compare_function(lowest, model)
    return function(p1, p2) -- anonymous function

        -- Determine the orientation of the triplet (lowest, p1, p2)
        local o = orientation(lowest, p1, p2, model)

        -- If p1 and p2 are collinear with lowest, choose the farther one to lowest
        if o == 0 then
            return squared_distance(lowest, p1) < squared_distance(lowest, p2)
        end

        -- For non-collinear points, choose the one that forms a counterclockwise turn with lowest
        return o == 2
    end
end

-- Function to find the orientation of ordered triplet (p, q, r).
-- The function returns the following values:
-- 0 : Collinear points
-- 1 : Clockwise points
-- 2 : Counterclockwise  
function orientation(p, q, r, model)
    -- print the vectors and val
    -- print_vertices({p, q, r}, "Orientation", model)
    local val = (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y)
    -- print(val, "Orientation", model)
    if val == 0 then return 0  -- Collinear
    elseif val > 0 then return 2  -- Counterclockwise
    else return 1  -- Clockwise
    end
end

function convex_hull(points, model)
    local n = #points
    if n < 3 then return {} end  -- Less than 3 points cannot form a convex hull

    -- Find the point with the lowest y-coordinate (or leftmost in case of a tie)
    local lowest = 1
    for i = 2, n do
        if points[i].y < points[lowest].y or (points[i].y == points[lowest].y and points[i].x < points[lowest].x) then
            lowest = i
        end
    end

    -- Swap the lowest point to the start of the array
    points[1], points[lowest] = points[lowest], points[1]

    -- Sort the rest of the points based on their polar angle with the lowest point
    local compare = create_compare_function(points[1], model) -- closure over the lowest point
    table.sort(points, compare)

    -- Sorted points are necessary but not sufficient to form a convex hull.
    --! The stack is used to maintain the vertices of the convex hull in construction.

    -- Initializing stack with the first three sorted points
    -- These form the starting basis of the convex hull.
    local stack = {points[1], points[2], points[3]}
    local non_stack = {}

    -- Process the remaining points to build the convex hull
    for i = 4, n do
        -- Check if adding the new point maintains the convex shape.
        -- Remove points from the stack if they create a 'right turn'.
        -- This ensures only convex shapes are formed.
        while #stack > 1 and orientation(stack[#stack - 1], stack[#stack], points[i]) ~= 2 do
            table.remove(stack)
        end
        table.insert(stack, points[i])  -- Add the new point to the stack
    end

    -- The stack now contains the vertices of the convex hull in counterclockwise order.
    return stack
end
-- ========================================================================================================================

function create_shape_from_vertices(v, model)
	local shape = {type="curve", closed=true;}
	for i=1, #v-1 do
		table.insert(shape, {type="segment", v[i], v[i+1]})
	end
  	table.insert(shape, {type="segment", v[#v], v[1]})
	return shape
end

function point_on_segment(p, s)
    local cross_product = (p.x - s[1].x) * (s[2].y - s[1].y) - (p.y - s[1].y) * (s[2].x - s[1].x)
    if cross_product ~= 0 then return false end

    local dot_product = (p.x - s[1].x) * (s[2].x - s[1].x) + (p.y - s[1].y) * (s[2].y - s[1].y)
    if dot_product < 0 then return false end
    if dot_product > squared_distance(s[1], s[2]) then return false end

    return true
end

function create_segments_from_vertices(vertices)
	local segments_start_finish = {}
	for i=1, #vertices-1 do
		table.insert( segments_start_finish, {vertices[i],vertices[i+1]} )
	end

	table.insert( segments_start_finish, {vertices[#vertices], vertices[1]} )
	return segments_start_finish
end

function not_in_table(t, v)
    for i=1, #t do
        if t[i] == v then return false end
    end
    return true
end


local creation_objects = {}
function onion_peeling(points, model)

    if points == nil or #points <= 1 then return end
    if #points == 2 then
        table.insert(creation_objects, create_shape_from_vertices(points, model))
        return
    end
    local hull = convex_hull(points, model)
    local shape = create_shape_from_vertices(hull, model)
    table.insert(creation_objects, shape)

    
    local non_hull = {}
    local segments = create_segments_from_vertices(hull)
    for i=1, #points do
        if not_in_table(hull, points[i]) then
            local on_boundary = false
            for j=1, #segments do
                if point_on_segment(points[i], segments[j]) then
                    on_boundary = true
                    break
                end
            end
            if not on_boundary then table.insert(non_hull, points[i]) end
        end
    end
    
    onion_peeling(non_hull, model)
end

function runOnionPeeling(model)
    local p = model:page()
    if not p:hasSelection() then incorrect("Please select at least 1 points", model) return end

	local referenceObjects = {}
	local count = 0
	for _, obj, sel, _ in p:objects() do
		if sel then
		count = count + 1
			if obj:type() ~= "reference" then
				incorrect("One or more selections are not points", model)
				return
			else
				table.insert(referenceObjects, obj:matrix() * obj:position())
			end
		end
	end
	
    if count < 1 then incorrect("Please select at least 1 points", model) return end

    
    onion_peeling(referenceObjects, model)
    
    for i=1, #creation_objects do
        local shape = creation_objects[i]
        model:creation("onion peeling", ipe.Path(model.attributes, {shape}))
    end
    creation_objects = {};
end

back to top

Software Heritage — Copyright (C) 2015–2026, The Software Heritage developers. License: GNU AGPLv3+.
The source code of Software Heritage itself is available on our development forge.
The source code files archived by Software Heritage are available under their own copyright and licenses.
Terms of use: Archive access, API— Content policy— Contact— JavaScript license information— Web API