Module:CineMol/style
Jump to navigation
Jump to search
File:Test Template Info-Icon - Version (2).svg Module documentation[create] [purge]
-- 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 colorFunctions = {
to_hex = function(self)
checkType( 'color:to_hex', 1, self, 'Color' )
return string.format( "#%02x%02x%02x", self.r, self.g, self.b)
end,
diffuse = function(self, alpha)
checkType( 'color:diffuse', 1, self, 'Color' )
checkType( 'color:diffuse', 2, alpha, 'number' )
alpha = math.max(0, math.min(1, alpha))
return p.Color(math.floor(self.r*alpha),math.floor(self.g*alpha),math.floor(self.b*alpha))
end
}
local colorEqual = function(self, other)
checkType( 'color==', 1, self, 'Color' )
checkType( 'color==', 2, other, 'Color' )
return self.r == other.r and self.g == other.g and self.b == other.b
end
function p.Color(r, g, b)
checkType( 'Color', 1, r, 'number' )
checkType( 'Color', 2, g, 'number' )
checkType( 'Color', 3, b, 'number' )
local obj = {
r = math.floor(r),
g = math.floor(g),
b = math.floor(b),
_TYPE = 'Color'
}
setmetatable( obj,
{
__index = colorFunctions,
__eq = colorEqual,
__tostring = colorFunctions.to_hex
}
)
return obj
end
local Color = p.Color
-- Corey-Pauling-Koltun coloring convention for atoms.
-- Source: https://en.wikipedia.org/wiki/CPK_coloring
p.CoreyPaulingKoltungAtomColor = {
H = Color(255, 255, 255),
C = Color(80, 80, 80),
N = Color(0, 0, 255),
O = Color(255, 0, 0),
P = Color(255, 165, 0),
S = Color(255, 255, 0),
B = Color(245, 245, 220),
Br = Color(139, 0, 0),
I = Color(148, 0, 211),
Ti = Color(128, 128, 128),
Fe = Color(255, 140, 0),
F = Color(0, 128, 0),
Cl = Color(0, 128, 0),
He = Color(0, 255, 255),
Ne = Color(0, 255, 255),
Ar = Color(0, 255, 255),
Kr = Color(0, 255, 255),
Xe = Color(0, 255, 255),
Li = Color(238, 130, 238),
Na = Color(238, 130, 238),
K = Color(238, 130, 238),
Rb = Color(238, 130, 238),
Cs = Color(238, 130, 238),
Fr = Color(238, 130, 238),
Be = Color(0, 100, 0),
Mg = Color(0, 100, 0),
Ca = Color(0, 100, 0),
Sr = Color(0, 100, 0),
Ba = Color(0, 100, 0),
Ra = Color(0, 100, 0),
Cd = Color(170, 51, 106),
_TYPE = 'AtomColoringScheme',
get_color = function(self, atom_symbol)
checkType( 'get_color', 1, self, 'AtomColoringScheme' )
checkType( 'get_color', 2, atom_symbol, 'string' )
return self[atom_symbol] or Color(255, 192, 203)
end
}
-- Atomic radii (van der Waals) in picometer from PubChem.
-- Source: https://pubchem.ncbi.nlm.nih.gov/periodic-table/#property=AtomicRadius
p.PubChemAtomRadius = {
H = 120.0,
He = 140.0,
Li = 182.0,
Be = 153.0,
B = 192.0,
C = 170.0,
N = 155.0,
O = 152.0,
F = 135.0,
Ne = 154.0,
Na = 227.0,
Mg = 173.0,
Al = 184.0,
Si = 210.0,
P = 180.0,
S = 180.0,
Cl = 175.0,
Ar = 188.0,
K = 275.0,
Ca = 231.0,
Sc = 211.0,
Ti = 187.0,
V = 179.0,
Cr = 189.0,
Mn = 197.0,
Fe = 194.0,
Co = 192.0,
Ni = 163.0,
Cu = 140.0,
Zn = 139.0,
Ga = 187.0,
Ge = 211.0,
As = 185.0,
Se = 190.0,
Br = 183.0,
Kr = 202.0,
Rb = 303.0,
Sr = 249.0,
Y = 219.0,
Zr = 186.0,
Nb = 207.0,
Mo = 209.0,
Tc = 209.0,
Ru = 207.0,
Rh = 195.0,
Pd = 202.0,
Ag = 172.0,
Cd = 158.0,
In = 193.0,
Sn = 217.0,
Sb = 206.0,
Te = 206.0,
I = 198.0,
Xe = 216.0,
Cs = 343.0,
Ba = 268.0,
Lu = 221.0,
Hf = 212.0,
Ta = 217.0,
W = 210.0,
Re = 217.0,
Os = 216.0,
Ir = 202.0,
Pt = 209.0,
Au = 166.0,
Hg = 209.0,
Tl = 196.0,
Pb = 202.0,
Bi = 207.0,
Po = 197.0,
At = 202.0,
Rn = 220.0,
Fr = 348.0,
Ra = 283.0,
_TYPE = 'AtomRadiusScheme',
--Return the radius of an atom in angstrom.
to_angstrom = function (self, atom_symbol)
assert( type(self) == 'table', 'arg 1 must be table' )
assert( type(atom_symbol) == 'string', 'arg 2 must be string')
local factor = 0.01 -- pictometer to angstrom
return self[atom_symbol] ~= nil and self[atom_symbol]*factor or default*factor
end
}
-- ==================
-- Art styles
-- ==================
function p.Cartoon( fill_color, outline_color, outline_width, opacity )
checkType( 'Catroon', 1, fill_color, 'Color' )
outline_color = outline_color == nil and Color(0,0,0) or outline_color
outline_width = outline_width == nil and 0.05 or outline_width
opacity = opacity == nil and 1.0 or opacity
checkType( 'Cartoon', 2, outline_color, 'Color' )
checkType( 'Cartoon', 3, outline_width, 'number' )
checkType( 'Cartoon', 4, opacity, 'number' )
return {
_TYPE = 'Depiction',
name = 'Cartoon',
fill_color = fill_color,
outline_width = outline_width,
outline_color = outline_color,
opacity = opacity
}
end
function p.Glossy( fill_color, opacity )
checkType( 'Catroon', 1, fill_color, 'Color' )
opacity = opacity == nil and 1.0 or opacity
checkType( 'Cartoon', 2, opacity, 'number' )
return {
_TYPE = 'Depiction',
name = 'Glossy',
fill_color = fill_color,
opacity = opacity
}
end
-- FillStyle
function p.Wire(stroke_color, stroke_width, opacity)
checkType( 'Wire', 1, stroke_color, 'Color' )
checkType( 'Wire', 2, stroke_width, 'number' )
checkType( 'Wire', 3, opacity, 'number' )
return {
_TYPE = 'FillStyle',
name = 'Wire',
stroke_color = stroke_color,
stroke_width = stroke_width,
opacity = opacity
}
end
function p.Solid(fill_color, stroke_color, stroke_width, opacity)
checkType( 'Solid', 1, fill_color, 'Color' )
checkType( 'Solid', 2, stroke_color, 'Color' )
checkType( 'Solid', 3, stroke_width, 'number' )
checkType( 'Solid', 4, opacity, 'number' )
return {
_TYPE = 'FillStyle',
name = 'Solid',
fill_color = fill_color,
stroke_color = stroke_color,
stroke_width = stroke_width,
opacity = opacity
}
end
function p.RadialGradient(fill_color, center, radius, opacity)
checkType( 'Wire', 1, fill_color, 'Color' )
checkType( 'Wire', 2, center, 'Point2D' )
checkType( 'Wire', 3, radius, 'number' )
checkType( 'Wire', 4, opacity, 'number' )
return {
_TYPE = 'FillStyle',
name = 'RadialGradient',
fill_color = fill_color,
radius = radius,
center = center,
opacity = opacity
}
end
function p.LinearGradient(fill_color, start, endp, opacity)
checkType( 'Wire', 1, fill_color, 'Color' )
checkType( 'Wire', 2, start, 'Point2D' )
checkType( 'Wire', 3, endp, 'Point2D' )
checkType( 'Wire', 4, opacity, 'number' )
return {
_TYPE = 'FillStyle',
name = 'LinearGradient',
fill_color = fill_color,
start = start,
endp = endp,
opacity = opacity
}
end
function p.Fill( reference, fill_style )
assert(type(reference) == 'string', 'first argument should be string' )
checkType( 'Fill', 2, fill_style, 'FillStyle' )
local obj = {
reference = reference,
fill_style = fill_style
}
function obj:to_svg()
if self.fill_style.name == 'Wire' then
local stroke_color = self.fill_style.stroke_color:to_hex()
local stroke_width = self.fill_style.stroke_width
local opacity = self.fill_style.opacity
local style_str = (
"." .. self.reference ..
"{stroke:" .. stroke_color .. ";" ..
string.format( "stroke-width:%.3fpx;", stroke_width ) ..
string.format( "stroke-opacity:%.1f;", opacity ) ..
"stroke-linecap:round;" ..
"stroke-linejoin:round;}"
)
local definition_str = nil
return style_str, definition_str
elseif self.fill_style.name == 'Solid' then
local fill_color = self.fill_style.fill_color:to_hex()
local stroke_color = self.fill_style.stroke_color:to_hex()
local stroke_width = self.fill_style.stroke_width
local opacity = self.fill_style.opacity
local style_str = "." .. self.reference ..
"{fill:" .. fill_color .. ";" ..
"stroke:" .. stroke_color .. ";" ..
string.format( "stroke-width:%.3fpx;", stroke_width ) ..
string.format( "opacity:%.1f;}", opacity )
return style_str, nil
elseif self.fill_style.name == 'RadialGradient' then
local cx = self.fill_style.center.x
local cy = self.fill_style.center.y
local r = self.fill_style.radius
local stop_color_offset_a = self.fill_style.fill_color:to_hex()
local stop_color_offset_b = self.fill_style.fill_color:diffuse(0.75):to_hex()
local style_str = "." .. self.reference .. "{fill:url(#" .. self.reference .. ");}"
local definition_str = (
"<radialGradient" ..
string.format(' id="%s"', self.reference) ..
string.format(' cx="%.3f" cy="%.3f"', cx, cy ) ..
string.format(' r="%.3f" fx="%.3f" fy="%.3f"', r, cx, cy ) ..
' gradientTransform="matrix(1,0,0,1,0,0)"' ..
' gradientUnits="userSpaceOnUse"' ..
-- force it to be 1.0 for easier diffing results with python version
string.format(' opacity="%s">', self.fill_style.opacity == 1 and '1.0' or self.fill_style.opacity ) ..
string.format('<stop offset="0.00" stop-color="%s"/>', stop_color_offset_a ) ..
string.format('<stop offset="1.00" stop-color="%s"/>', stop_color_offset_b ) ..
"</radialGradient>"
)
return style_str, definition_str
elseif self.fill_style.name == 'LinearGradient' then
local multiplier = 3.0 -- Makes linear gradient look better.
local x1 = self.fill_style.start.x * multiplier
local y1 = self.fill_style.start.y * multiplier
local x2 = self.fill_style.endp.x * multiplier
local y2 = self.fill_style.endp.y * multiplier
local stop_color_offset_a = self.fill_style.fill_color:to_hex()
local stop_color_offset_b = self.fill_style.fill_color:diffuse(0.75):to_hex()
local style_str = string.format( ".%s{fill:url(#%s);}", self.reference, self.reference )
local definition_str = (
"<linearGradient" ..
string.format( ' id="%s"', self.reference ) ..
string.format( ' x1="%.3f" y1="%.3f"', x1, y1 ) ..
string.format( ' x2="%.3f" y2="%.3f"', x2, y2 ) ..
' gradientUnits="userSpaceOnUse"' ..
' spreadMethod="reflect"' ..
' gradientTransform="rotate(90)"' ..
string.format( ' opacity="%.1f">', self.fill_style.opacity ) ..
string.format( '<stop offset="0.00" stop-color="%s"/>', stop_color_offset_a ) ..
string.format( '<stop offset="1.00" stop-color="%s"/>', stop_color_offset_b ) ..
"</linearGradient>"
)
return style_str, definition_str
else
error( "Unrecognized fill style - " .. self.fill_style.name )
end
end
return obj
end
return p