Module:FunctionGraph
外观
函數
[编辑源代码]functionGraph
[编辑源代码]一般函數圖形
{{#invoke:FunctionGraph|functionGraph|Template:新增條文|start=-5|end=5}}
complex_graph
[编辑源代码]複變函數圖形(色相環複變函數圖形)。因要向2個維度取樣(如取樣數設定為100則要運算100×100=10000次,更高的取樣數可能會超出WP:模板限制)因此較耗費效能,請斟酌使用。
{{#invoke:FunctionGraph|complex_graph|x}}
{{#invoke:FunctionGraph|complex_graph|sin(1/x)|xstart=-0.5|xend=0.5|ystart=-0.5|yend=0.5}}
local p = {}
local comp_number = nil
local getArgs = require('Module:Arguments').getArgs
local lib_calc = require('Module:Complex_Number/Calculate')
local TrackingCategory = require('Module:TrackingCategory')
local noop_func = function()end
function p.applyFunctionGraph(frame)
local working_frame = mw.getCurrentFrame()
local body = working_frame:extensionTag{ name = 'graph', content = require('Module:FunctionGraph/Graph').chart(frame) }
return body
end
function p.functionGraph(frame)
if comp_number == nil then comp_number = require("Module:Complex Number") end
local cmath, qmath = comp_number.cmath.init(), comp_number.qmath.init()
if not getArgs then getArgs = require('Module:Arguments').getArgs end
local args = getArgs(frame, {parentFirst=true})
local exprs = {}
local body_args, x_start, x_end, y_min, y_max, sampling = {}, 0, 1, nil, nil, 50
for arg_name, arg_value in pairs( args ) do
local check_arg_name = mw.ustring.lower(tostring(arg_name))
if tonumber(arg_name) ~= nil then exprs[#exprs + 1] = lib_calc._remove_strip_marker(arg_value)
elseif check_arg_name == "start" then x_start = tonumber(arg_value)
elseif check_arg_name == "end" then x_end = tonumber(arg_value)
elseif check_arg_name == "sampling" then sampling = tonumber(arg_value)
elseif check_arg_name == "min" then y_min = tonumber(arg_value)
elseif check_arg_name == "max" then y_max = tonumber(arg_value)
else body_args[arg_name] = arg_value
end
end
local yesno = require('Module:Yesno')
if yesno(args.useOtherModule or 'no') == true then lib_calc.use_other_module = true end
local math_class = frame.args['class']or''
local mymath = cmath
local mytomath = cmath.toComplexNumber
if mw.ustring.sub(math_class,1,7):upper()=="MODULE:" then
local module_name, math_lib_name = lib_calc.checkModuleClass(math_class)
xpcall(function()
local load_module = require("Module:"..module_name)
if load_module ~= nil then
local load_math_lib = load_module[math_lib_name]
if load_module ~= nil then
local func_type = type(noop_func)
local my_math_lib = (type(load_math_lib.init) == func_type) and load_math_lib.init() or load_math_lib
if type(my_math_lib.constructor) == func_type then
math_class = "mymath"
mymath = my_math_lib
mytomath = my_math_lib.constructor
end
end
end
end,noop_func)
end
local body = p._functionGraph(exprs,
x_start, x_end, sampling, y_min, y_max, body_args, ( {
cmath = cmath,
qmath = qmath,
mymath = mymath,
} ) [math_class] , (( {
cmath = cmath.toComplexNumber,
qmath = qmath.toQuaternionNumber,
mymath = mytomath,
} ) [math_class] ) )
body.width = 400
body.height = 100; body.type="line"
body.interpolate = frame.args['interpolate'] or "monotone"
for arg_name, arg_value in pairs( body_args ) do
body[arg_name] = arg_value
end
--body = mw.getCurrentFrame():expandTemplate{title = "Graph:Chart", args = body}
body = p.applyFunctionGraph(body)
if use_ext_mathlib == true then TrackingCategory.append('使用擴充複變函數庫的頁面') end
return body
end
function p.calc_table(frame)
local variable_process = require("Module:Number/data")
local args
local can_math = false
local should_math = false
local show_math = false
if frame == mw.getCurrentFrame() then
-- We're being called via #invoke. The args are passed through to the module
-- from the template page, so use the args that were passed into the template.
args = getArgs(frame, {
parentFirst=true,
trim = false,
removeBlanks = false
}) --frame.args
local yesno = require('Module:Yesno')
can_math = yesno(args['use math'] or args['use_math'])
should_math = yesno(args['should math'] or args['should_math'])
else
-- We're being called from another module or from the debug console, so assume
-- the args are passed in directly.
args = frame
end
lib_calc._randomseed()
local yesno = require('Module:Yesno')
if yesno(args.useOtherModule or 'no') == true then lib_calc.use_other_module = true end
local first_number_list = {"2","3","4","5","6","7","8","9"}
local second_number_list, first_number_show, second_number_show
local calculate_str, calculate_title = "{{{left}}} * {{{right}}}", "×"
if args['number list'] or args['number_list'] then first_number_list = mw.text.split(args['number list'] or args['number_list'] or '',',') end
if args['number list2'] or args['number_list2'] then second_number_list = mw.text.split(args['number list2'] or args['number_list2'] or '',',') end
if args['number list show'] or args['number_list_show'] then
first_number_show = mw.text.split(args['number list show'] or args['number_list_show'] or '',',')
if #first_number_show == 1 and mw.text.trim(first_number_show[1]) == '' then first_number_show = {} end
end
if args['number list show2'] or args['number_list_show2'] then
second_number_show = mw.text.split(args['number list show2'] or args['number_list_show2'] or '',',')
if #second_number_show == 1 and mw.text.trim(second_number_show[1]) == '' then second_number_show = {} end
end
if args['calculate'] then calculate_str = mw.text.trim(lib_calc._remove_strip_marker(args['calculate'])) end
if args['calculate title'] or args['calculate_title'] then calculate_title = mw.text.trim(args['calculate title'] or args['calculate_title']) end
if second_number_list == nil or (second_number_list and (second_number_list == {} or #second_number_list == 0) ) then
second_number_list = first_number_list
end
if first_number_show == nil or (first_number_show and (first_number_show == {} or #first_number_show == 0) ) then
first_number_show = first_number_list
end
if second_number_show == nil or (second_number_show and (second_number_show == {} or #second_number_show == 0) ) then
second_number_show = first_number_show
end
local body, buffer_str, table_str = "", '', ''
if comp_number == nil then comp_number = require("Module:Complex Number") end
local cmath, qmath, bmath = comp_number.cmath.init(), comp_number.qmath.init(), comp_number.bmath.init()
local mathtag = lib_calc.tagmath.init()
local math_class = args['class']or''
if mw.text.trim(math_class) == '' then math_class = "cmath" end
local mymath = cmath
local mytomath = cmath.toComplexNumber
if mw.ustring.sub(math_class,1,7):upper()=="MODULE:" then
local module_name, math_lib_name = lib_calc.checkModuleClass(math_class)
xpcall(function()
local load_module = require("Module:"..module_name)
if load_module ~= nil then
local load_math_lib = load_module[math_lib_name]
if load_module ~= nil then
local func_type = type(noop_func)
local my_math_lib = (type(load_math_lib.init) == func_type) and load_math_lib.init() or load_math_lib
if type(my_math_lib.constructor) == func_type then
math_class = "mymath"
mymath = my_math_lib
mytomath = my_math_lib.constructor
end
end
end
end,noop_func)
end
body = body .. "! " .. (args["main head css"] or args["main_head_css"] or '') .. " | " .. calculate_title .. ' \n'
for j=1,#second_number_list do
local second_it = mw.text.trim(second_number_list[j] or '')
local second_it_show = mw.text.trim(second_number_show[j] or '')
if second_it ~= nil then
local second_num_math = tostring(second_it_show or second_it)
if should_math or can_math then
if args.class ~= "mathtag" and yesno(args['show math'] or args['show_math']) then
second_num_math = lib_calc._re_math_output(second_num_math)
end
second_num_math = lib_calc._adj_math_output(second_num_math)
end
if can_math then second_num_math = frame:callParserFunction{name = "#tag:math", args = {second_num_math}} end
body = body .. "! " .. (args["head css"] or args["head_css"] or '') .. " | " .. second_num_math .. ' \n'
end
end
body = body .. '\n'
for i=1,#first_number_list do
local first_it = mw.text.trim(first_number_list[i] or '')
local first_it_show = mw.text.trim(first_number_show[i] or '')
if first_it ~= nil then
body = body .. "|-\n"; table_str = ''
local first_num_math = tostring(first_it_show or first_it)
if should_math or can_math then
if args.class ~= "mathtag" and yesno(args['show math'] or args['show_math']) then
first_num_math = lib_calc._re_math_output(first_num_math)
end
first_num_math = lib_calc._adj_math_output(first_num_math)
end
if can_math then first_num_math = frame:callParserFunction{name = "#tag:math", args = {first_num_math}} end
body = body .. "! " .. (args["head css"] or args["head_css"] or '') .. " | " .. first_num_math .. " \n"
for j=1,#second_number_list do
local second_it = mw.text.trim(second_number_list[j] or '')
if second_it ~= nil then
buffer_str = variable_process._getFormatingStringByArgument(calculate_str, {
left=tostring(first_it),right=tostring(second_it)
})
local exec_result = lib_calc.calc( buffer_str or '', ( {
cmath = cmath,
qmath = qmath,
bmath = bmath,
mathtag = mathtag,
mymath = mymath
} ) [ math_class ] , (( {
cmath = cmath.toComplexNumber,
qmath = qmath.toQuaternionNumber,
bmath = bmath.toBoolean,
mathtag = mathtag.toTagMath,
mymath = mytomath
} ) [ math_class ] ) )
local exec_result_str = mw.text.trim(tostring(exec_result) or '')
local exec_check = mw.text.trim((tonumber(exec_result_str) and args[tonumber(exec_result_str)]) or args[exec_result_str] or '')
if exec_check == '' then exec_check = nil end
table_str = table_str .. '|' .. (exec_check or args["number css"] or args["number_css"] or '') .. "| "
if should_math or can_math then
if args.class ~= "mathtag" and yesno(args['show math'] or args['show_math']) then
exec_result_str = lib_calc._re_math_output(exec_result_str)
end
exec_result_str = lib_calc._adj_math_output(exec_result_str)
end
if can_math then exec_result_str = frame:callParserFunction{name = "#tag:math", args = {exec_result_str}} end
table_str = table_str .. mw.text.trim(exec_result_str) .. ' \n'
end
end
body = body .. table_str
end
end
if use_ext_mathlib == true then TrackingCategory.append('使用擴充複變函數庫的頁面') end
return body
end
function p._functionGraph(expr_arr,x_start,x_end,sampling, y_min, y_max, body_args, math_lib, number_Constructer)
if (yesno or require('Module:Yesno'))((body_args or {}).useOtherModule or 'no') == true then lib_calc.use_other_module = true end
if comp_number == nil then comp_number = require("Module:Complex Number") end
math = comp_number.math.init()
lib_calc._randomseed()
local mathlib, numberConstructer = math_lib or math, number_Constructer or tonumber
local postfix = {}
local check_func = {}
local x_arr, y_arr = {}, {}
if type(expr_arr) == type({}) then
for i=1,#expr_arr do
local local_func_sign = '↦'
local check_parametric = mw.text.split(expr_arr[i],';')
--解決 函數-參數式 衝突
if mw.ustring.find(expr_arr[i], local_func_sign) then check_parametric = mw.text.split(expr_arr[i],'\\;')end
if #check_parametric == 1 then
local pre_expr, pre_scope = lib_calc._function_preprocessing(expr_arr[i], mathlib, numberConstructer, false)
postfix[#postfix + 1] = lib_calc.infixToPostfix(pre_expr, debug_flag)
if pre_scope then postfix[#postfix].scope = pre_scope end
elseif #check_parametric >= 3 then
postfix[#postfix + 1]={parametric=true}
postfix[#postfix].x_name = check_parametric[1] or 't'
postfix[#postfix].y_name = check_parametric[2] or 't'
postfix[#postfix].x = lib_calc.infixToPostfix(check_parametric[1] or 't', debug_flag)
postfix[#postfix].y = lib_calc.infixToPostfix(check_parametric[2] or 't', debug_flag)
postfix[#postfix].t = mw.text.trim(check_parametric[3] or 't')
postfix[#postfix].min = numberConstructer(check_parametric[4]) or numberConstructer(0)
postfix[#postfix].max = numberConstructer(check_parametric[5]) or numberConstructer(1)
end
y_arr[#y_arr + 1] = {}
x_arr[#x_arr + 1] = {}
end
else
local local_func_sign = '↦'
local check_parametric = mw.text.split(expr_arr,';')
--解決 函數-參數式 衝突
if mw.ustring.find(expr_arr, local_func_sign) then check_parametric = mw.text.split(expr_arr,'\\;')end
if #check_parametric == 1 then
local pre_expr, pre_scope = lib_calc._function_preprocessing(expr_arr, mathlib, numberConstructer, false)
postfix[#postfix + 1] = lib_calc.infixToPostfix(pre_expr, debug_flag)
if pre_scope then postfix[#postfix].scope = pre_scope end
elseif #check_parametric >= 3 then
postfix[#postfix + 1]={parametric=true}
postfix[#postfix].x_name = check_parametric[1] or 't'
postfix[#postfix].y_name = check_parametric[2] or 't'
postfix[#postfix].x = lib_calc.infixToPostfix(check_parametric[1] or 't', debug_flag)
postfix[#postfix].y = lib_calc.infixToPostfix(check_parametric[2] or 't', debug_flag)
postfix[#postfix].t = check_parametric[3] or 't'
postfix[#postfix].min = numberConstructer(check_parametric[4]) or numberConstructer(0)
postfix[#postfix].max = numberConstructer(check_parametric[5]) or numberConstructer(1)
end
y_arr[#y_arr + 1] = {}
x_arr[#x_arr + 1] = {}
end
local check_cexpr = mw.title.new("cexpr","template"):getContent()
local check_isreal = mw.title.new("isReal","template"):getContent()
local check_ifNumeric = mw.title.new("ifNumeric","template"):getContent()
for i=0,sampling do
local it = x_start + (i * (x_end-x_start) / sampling)
local x_val = it
for j=1,#expr_arr do
local calc_val = " "
xpcall(function()
if type(postfix[j]) == type({}) and postfix[j].parametric == true then
local it_t = postfix[j].min + (i * (postfix[j].max - postfix[j].min) / sampling)
--參數式
calc_val = lib_calc.calc_by_postfix(postfix[j].y, {[postfix[j].t]=it_t}, mathlib, numberConstructer, false)
x_val = lib_calc.calc_by_postfix(postfix[j].x, {[postfix[j].t]=it_t}, mathlib, numberConstructer, false)
else
calc_val = lib_calc.calc_by_postfix(postfix[j], {
x=it,
last=function(num) --for Template:數列
local last_num = (body_args or {})['last' .. tonumber(tostring(num or 1))] or 0
return numberConstructer(y_arr[j][#(y_arr[j])-(tonumber(tostring(num))or 1)+1] or last_num)
end,
}, mathlib, numberConstructer, false)
if( tonumber((body_args or {})["calc diff " .. tostring(j) ]) == 1 or ((yesno or require('Module:Yesno'))((body_args or {})["calc diff " .. tostring(j) ]or'no')==true) )then
local dy = lib_calc.calc_by_postfix(postfix[j], {x=(it + 1e-6)}, mathlib, numberConstructer, false)
calc_val = 1e6 * (dy - calc_val)
end
end
if y_max and mathlib.re(calc_val) > y_max then calc_val = y_max end
if y_min and mathlib.re(calc_val) < y_min then calc_val = y_min end
if x_end and mathlib.re(x_val) > x_end then x_val = x_end end
if x_start and mathlib.re(x_val) < x_start then x_val = x_start end
end,function(_)end)
if tonumber((body_args or {})["round number"]) ~= nil then
if calc_val then
calc_val = mathlib.round(calc_val, tonumber((body_args or {})["round number"]), 10)
end
end
if tonumber((body_args or {})["nonreal is nan"]) == 1 then
if math.abs(tonumber(mathlib.abs(mathlib.nonRealPart(calc_val))) or 0) > 1e-14 then calc_val = nil end
end
local num_check = mw.ustring.lower(tostring(numberConstructer(calc_val)))
if mw.ustring.match(num_check,"nan") or mw.ustring.match(num_check,"nil") or mw.ustring.match(num_check,"inf") then calc_val = ' ' end
num_check = mw.ustring.lower(tostring(numberConstructer(x_val)))
if mw.ustring.match(num_check,"nan") or mw.ustring.match(num_check,"nil") or mw.ustring.match(num_check,"inf") then x_val = ' ' end
y_arr[j][ (#(y_arr[j]) + 1) ] = tostring(calc_val)
x_arr[j][ (#(x_arr[j]) + 1) ] = tostring(x_val)
end
end
local result={}
if #expr_arr > 0 then result.legend = "函數" end
for i=1,#expr_arr do
result['x'] = table.concat(x_arr[i],',')
result['y' .. tostring(i)] = table.concat(y_arr[i],',')
result['y' .. tostring(i) .. "Title"] = tostring( (body_args or {})[tostring(i) .. " name" ] or expr_arr[i] )
if type(postfix[i]) == type({}) and postfix[i].parametric == true then
result['y' .. tostring(i) .. "Title"] = "x=" .. postfix[i].x_name .. "; y=" .. postfix[i].y_name
elseif( tonumber((body_args or {})["calc diff " .. tostring(i) ]) == 1 )then
result['y' .. tostring(i) .. "Title"] = '( ' .. result['y' .. tostring(i) .. "Title"] .. " )\'"
end
if check_func[ result['y' .. tostring(i) .. "Title"] ] ~= nil then
local new_name = result['y' .. tostring(i) .. "Title"] .. " ,(" .. tostring(check_func[ result['y' .. tostring(i) .. "Title"] ]+1) .. ")"
check_func[ result['y' .. tostring(i) .. "Title"] ] = check_func[ result['y' .. tostring(i) .. "Title"] ] + 1
result['y' .. tostring(i) .. "Title"] = new_name
else check_func[ result['y' .. tostring(i) .. "Title"] ] = 1
end
end
return result
end
function p.complex_graph(frame)
if comp_number == nil then comp_number = require("Module:Complex Number") end
local cmath, qmath = comp_number.cmath.init(), comp_number.qmath.init()
if not getArgs then getArgs = require('Module:Arguments').getArgs end
local args = getArgs(frame, {parentFirst=true})
local expr = args[1] or args['1'] or 'x'
local body_args, x_start, x_end, y_start, y_end, sampling, width = {}, -5, 5, -5, 5, 100, 120
for arg_name, arg_value in pairs( args ) do
local check_arg_name = mw.ustring.gsub(mw.ustring.lower(tostring(arg_name)), "[ _]", "")
if check_arg_name == "xstart" then x_start = tonumber(arg_value)
elseif check_arg_name == "xend" then x_end = tonumber(arg_value)
elseif check_arg_name == "ystart" then y_start = tonumber(arg_value)
elseif check_arg_name == "yend" then y_end = tonumber(arg_value)
elseif check_arg_name == "sampling" then sampling = tonumber(arg_value)
elseif check_arg_name == "width" then width = tonumber(arg_value)
else body_args[arg_name] = arg_value
end
end
local yesno = require('Module:Yesno')
if yesno(args.useOtherModule or 'no') == true then lib_calc.use_other_module = true end
local math_class = frame.args['class']or''
local mymath = cmath
local mytomath = cmath.toComplexNumber
if mw.ustring.sub(math_class,1,7):upper()=="MODULE:" then
local module_name, math_lib_name = lib_calc.checkModuleClass(math_class)
xpcall(function()
local load_module = require("Module:"..module_name)
if load_module ~= nil then
local load_math_lib = load_module[math_lib_name]
if load_module ~= nil then
local func_type = type(noop_func)
local my_math_lib = (type(load_math_lib.init) == func_type) and load_math_lib.init() or load_math_lib
if type(my_math_lib.constructor) == func_type then
math_class = "mymath"
mymath = my_math_lib
mytomath = my_math_lib.constructor
end
end
end
end,noop_func)
end
local calc_result = p._complex_graph(expr,
x_start, x_end, y_start, y_end, sampling ,width, body_args, ( {
cmath = cmath,
qmath = qmath,
mymath = mymath,
} ) [math_class] , (( {
cmath = cmath.toComplexNumber,
qmath = qmath.toQuaternionNumber,
mymath = mytomath,
} ) [math_class] ) )
local body = frame:callParserFunction{ name = '#tag:graph', args = {
mw.text.jsonEncode(calc_result)
} }
if use_ext_mathlib == true then TrackingCategory.append('使用擴充複變函數庫的頁面') end
return body
end
function p._complex_graph(expr, x_start, x_end, y_start, y_end, sampling, width, body_args, math_lib, number_Constructer)
if (yesno or require('Module:Yesno'))((body_args or {}).useOtherModule or 'no') == true then lib_calc.use_other_module = true end
if comp_number == nil then comp_number = require("Module:Complex Number") end
local cmath = comp_number.cmath.init()
lib_calc._randomseed()
local mathlib, numberConstructer = math_lib or cmath, number_Constructer or cmath.constructor
local pxsize = width
local pre_expr, pre_scope = lib_calc._function_preprocessing(expr, mathlib, numberConstructer, false)
local postfix = lib_calc.infixToPostfix(pre_expr, debug_flag)
if pre_scope then postfix.scope = pre_scope end
local function HSBToRGB(h, s, b)
local function k(n) return (n + h / 60) % 6 end
local function f(n) return b * (1 - s * math.max(0, math.min(k(n), 4 - k(n), 1))) end
return 255 * f(5), 255 * f(3), 255 * f(1);
end
local result = {}
local i_value = mathlib.elements and (mathlib.elements[2] and mathlib.elements[2] or mathlib.i) or mathlib.i
if not i_value then error("繪製失敗:所用數體無非實數單位元") end
for i=0,sampling do
local it_y = y_start + (i * (y_end-y_start) / sampling)
local calc_row = {}
for j=0,sampling-1 do
local it_x = x_start + (j * (x_end-x_start) / sampling)
local it = mathlib[0] + it_x + i_value * it_y
local calc_val = lib_calc.calc_by_postfix(postfix, {x=it}, mathlib, numberConstructer, false)
local check_nan = not not(tostring(calc_val):match("[%+%-]?[Nn][IiAa][LlNn]"))
local check_inf = not not(tostring(calc_val):match("[%+%-]?[Ii][Nn][Ff]"))
local result_color = "rgb(127,127,127)"
if not check_nan then
if check_inf then result_color = "rgb(255,255,255)"
else
local h, s, b = (mathlib.re(mathlib.arg(calc_val)) / math.pi * 180) % 360,
mathlib.re(mathlib.inverse(mathlib.log(mathlib.abs(calc_val) + 1) * 0.3 + 1)),
mathlib.re(-mathlib.inverse(mathlib.log(mathlib.abs(calc_val) + 1) * 5 + 1.1) + 1)
result_color = string.format("rgb(%d,%d,%d)", HSBToRGB(h + ((h < 0)and 360 or 0), s, b))
end
end
calc_row[#calc_row + 1] = result_color
end
result[#result + 1] = calc_row
end
local w_count = #result
local cell_width = pxsize / (w_count+2)
local graph_data = {
width=pxsize,
height=pxsize,
data={},
padding={left=cell_width,top=cell_width,bottom=cell_width,right=cell_width},
scales={
{name="x",type="linear",domain={0,w_count},range="width"},
{name="y",type="linear",domain={0,w_count},range="height"},
},
marks={
{
type="rect",
from={data="funcdata"},
properties={
enter={
x={scale="x",field="0"},y={scale="y",field="1"},
width={value=cell_width},height={value=cell_width},
stroke={field="2"},
fill={field="2"},
}
}
}
}
}
local func_data = {name="funcdata",values={}}
for x=1,w_count do
for y=1,#(result[x]) do
func_data.values[#(func_data.values)+1] = {x-1,#(result[x])-y+1,result[y][x]}
end
end
graph_data.data[#(graph_data.data)+1] = func_data
return graph_data
end
return p