Module:CineMol/svg

From Wikipedia, the free encyclopedia
Jump to navigation Jump to search
-- This is a port of CineMol to lua
-- CineMol https://github.com/moltools/CineMol was written by David Meijer, Marnix H. Medema & Justin J. J. van der Hooft and is MIT licensed
-- Please consider any edits made to this page as dual licensed MIT & CC-BY-SA 4.0

local p = {}
local Point2D = require( 'Module:CineMol/geometry' ).Point2D
local checkType = require( 'Module:CineMol/geometry' ).checkType
local Color = require( 'Module:CineMol/style' ).Color
local Fill = require( 'Module:CineMol/style' ).Fill

function p.Circle2D(reference, center, radius)
	checkType( 'Circle2D', 1, reference, 'string' )
	checkType( 'Circle2D', 2, center, 'Point2D' )
	checkType( 'Circle2D', 3, radius, 'number' )
	local obj = {
		_TYPE = 'Shape2D',
		reference = reference,
		center = center,
		radius = radius
	}
	function obj:to_svg()
		return string.format('<circle class="%s" cx="%.3f" cy="%.3f" r="%.3f"/>', self.reference, self.center.x, self.center.y, self.center.radius )
	end
	return obj
end


function p.Line2D(reference, start, endp)
	checkType( 'Line2D', 1, reference, 'string' )
	checkType( 'Line2D', 2, start, 'Point2D' )
	checkType( 'Line2D', 3, endp, 'Point2D' )
	local obj = {
		_TYPE = 'Shape2D',
		reference = reference,
		start = start,
		endp = endp
	}
	function obj:to_svg()
		return string.format(
			'<line class="%s" x1="%.3f" y1="%.3f" x2="%.3f" y2="%.2f"/>',
			self.reference, self.start.x, self.start.y, self.endp.x, self.endp.y
		)
	end
	return obj
end

function p.Polygon2D(reference, points)
	checkType( 'Line2D', 1, reference, 'string' )
	checkType( 'Line2D', 2, points, 'table' )
	assert( #points == 0 or points[1]._TYPE == 'Point2D', 'Wrong type for arg 2' )
	local obj = {
		_TYPE = 'Shape2D',
		reference = reference,
		points = points
	}
	function obj:to_svg()
		local pointList = ''
		for _,pt in ipairs(points) do
			pointList = pointList .. string.format( "%.3f,%.3f ", pt.x, pt.y )
		end
		if string.sub( pointList, -1 ) == ' ' then
			pointList = string.sub( pointList, 1, -2 )
		end
		return string.format(
			'<polygon class="%s" points="%s"/>',
			self.reference,
			pointList
		)
	end
	return obj
end

-- This is a bit different than CineMol.
function p.ViewBox( minx, miny, width, height )
	checkType( 'ViewBox', 1, minx, 'number' )
	checkType( 'ViewBox', 2, miny, 'number' )
	checkType( 'ViewBox', 3, width, 'number' )
	checkType( 'ViewBox', 4, height, 'number' )
	local obj = {
		_TYPE = 'ViewBox',
		miny = miny,
		minx = minx,
		width = width,
		height = height
	}
	function obj:to_value()
		return string.format("%.3f %.3f %.3f %.3f", minx, miny, width, height )
	end
	return obj
end

-- This is different then in CineMol
function p.Svg(view_box, window, background_color, fills, objects)
	checkType( 'Svg', 1, view_box, 'ViewBox' )
	fills = fills == nil and {} or fills
	objects = objects == nil and {} or objects
	local obj = {
		_TYPE = 'Svg',
		view_box = view_box,
		-- We maybe ignore this and handle our own sizing.
		window = window,
		background_color = background_color,
		fills = fills,
		objects = objects
	}

	function obj:header()
		local x = self.view_box.min_x
        local y = self.view_box.min_y
		local background = ""

        if self.background_color ~= nil then
            background =
                "<rect " ..
                string.format('x="%.3f" y="%.3f" ', x, y ) ..
                'width="100%" ' ..
                'height="100%" ' ..
                string.format( 'fill="%s"', self.background_color.to_hex() ) ..
                "/>"
		end

        return background
	end
	-- We do not return surounding <svg> tag
	function obj:to_svg()
		local header = self:header()
		local styles, definitions = {}, {}
        for i,fill in ipairs(self.fills) do
            local style, definition = fill:to_svg()

            assert( style ~= 'nil' )
            styles[#styles+1] = style

            if definition ~= nil then
                definitions[#definitions+1] = definition
			end
		end

        local styles_str = table.concat( styles, "\n" )
        local definitions_str = table.concat( definitions, "\n" )
		local objects_str = ""
		for i,object in ipairs( objects ) do
        	objects_str = objects_str .. object:to_svg() .. "\n"
		end

		return header .. "\n<defs>\n<style>\n" .. styles_str .. "\n</style>\n" .. definitions_str .. "\n</defs>\n" .. objects_str

	end

	function obj:to_mw_svg()
		return mw.svg.new()
			:setAttribute( 'viewBox', self.view_box:to_value() )
			:setContent( self:to_svg() )
	end

	return obj
end

return p