模組:沙盒/What7what8/func
外观
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
-- SVG-based graph rendering functions
function p.drawFunctionGraphSVG(graph_data)
local svg = mw.svg.new()
local width = graph_data.width or 400
local height = graph_data.height or 100
-- Set up SVG attributes - convert numbers to strings
svg:setAttribute('viewBox', '0 0 ' .. tostring(width) .. ' ' .. tostring(height))
svg:setImgAttribute('width', tostring(width))
svg:setImgAttribute('height', tostring(height))
svg:setImgAttribute('class', 'function-graph-svg')
-- Calculate padding for labels and titles
local left_padding = 40 -- Space for y-axis labels
local right_padding = 20
local top_padding = 30 -- Space for title
local bottom_padding = 40 -- Space for x-axis labels and title
local content_width = width - left_padding - right_padding
local content_height = height - top_padding - bottom_padding
local content_x = left_padding
local content_y = top_padding
-- Create the content
local content = ''
-- Draw background for content area
local background = mw.html.create('rect')
:attr('x', tostring(content_x))
:attr('y', tostring(content_y))
:attr('width', tostring(content_width))
:attr('height', tostring(content_height))
:attr('fill', '#f8f8f8')
:attr('stroke', '#e0e0e0')
:attr('stroke-width', '1')
content = content .. tostring(background)
-- Calculate axis ranges
local x_min = graph_data.x_min or 0
local x_max = graph_data.x_max or 1
local y_min = graph_data.y_min or 0
local y_max = graph_data.y_max or 1
-- Draw grid lines
local x_divisions = 8
local y_divisions = 8
for i = 0, x_divisions do
local x_pos = content_x + (i * content_width / x_divisions)
local y_pos = content_y + (i * content_height / y_divisions)
-- Vertical grid line
local v_line = mw.html.create('line')
:attr('x1', tostring(x_pos)):attr('y1', tostring(content_y))
:attr('x2', tostring(x_pos)):attr('y2', tostring(content_y + content_height))
:css('stroke', '#e8e8e8')
:css('stroke-width', '1px')
content = content .. tostring(v_line)
-- Horizontal grid line
local h_line = mw.html.create('line')
:attr('x1', tostring(content_x)):attr('y1', tostring(y_pos))
:attr('x2', tostring(content_x + content_width)):attr('y2', tostring(y_pos))
:css('stroke', '#e8e8e8')
:css('stroke-width', '1px')
content = content .. tostring(h_line)
-- X-axis labels
local x_value = x_min + (i * (x_max - x_min) / x_divisions)
local x_label = mw.html.create('text')
:attr('x', tostring(x_pos))
:attr('y', tostring(content_y + content_height + 15))
:attr('text-anchor', 'middle')
:attr('font-family', 'sans-serif')
:attr('font-size', '10px')
:attr('fill', '#333333')
:wikitext(string.format('%.2f', x_value))
content = content .. tostring(x_label)
-- Y-axis labels
local y_value = y_min + ((y_divisions - i) * (y_max - y_min) / y_divisions)
local y_label = mw.html.create('text')
:attr('x', tostring(content_x - 5))
:attr('y', tostring(y_pos + 3))
:attr('text-anchor', 'end')
:attr('font-family', 'sans-serif')
:attr('font-size', '10px')
:attr('fill', '#333333')
:wikitext(string.format('%.2f', y_value))
content = content .. tostring(y_label)
end
-- Draw axes at y=0 and x=0 if within range
local x_axis_y = content_y + content_height * (1 - (0 - y_min) / (y_max - y_min))
local y_axis_x = content_x + content_width * ((0 - x_min) / (x_max - x_min))
-- X-axis (y=0 line)
if y_min <= 0 and y_max >= 0 then
local x_axis = mw.html.create('line')
:attr('x1', tostring(content_x))
:attr('y1', tostring(x_axis_y))
:attr('x2', tostring(content_x + content_width))
:attr('y2', tostring(x_axis_y))
:css('stroke', '#333333')
:css('stroke-width', '1.5px')
content = content .. tostring(x_axis)
end
-- Y-axis (x=0 line)
if x_min <= 0 and x_max >= 0 then
local y_axis = mw.html.create('line')
:attr('x1', tostring(y_axis_x))
:attr('y1', tostring(content_y))
:attr('x2', tostring(y_axis_x))
:attr('y2', tostring(content_y + content_height))
:css('stroke', '#333333')
:css('stroke-width', '1.5px')
content = content .. tostring(y_axis)
end
-- Define colors for different series
local colors = {'#ff4444', '#4444ff', '#44aa44', '#ffaa00', '#aa44aa', '#aa6644',
'#44aaaa', '#aa4444', '#888844', '#aa88aa', '#4488aa', '#aa4488'}
-- Draw each function series
for i, series_data in ipairs(graph_data.series or {}) do
local series_type = series_data.type or "function"
local color = colors[(i-1) % #colors + 1]
if series_type == "function" then
-- Regular function - handle discontinuities properly
local x_points = mw.text.split(series_data.x or '', ',')
local y_points = mw.text.split(series_data.y or '', ',')
if #x_points == #y_points and #x_points > 1 then
-- Group continuous segments
local segments = {}
local current_segment = {}
for j = 1, #x_points do
local x = tonumber(x_points[j])
local y = tonumber(y_points[j])
if x and y and y ~= 0/0 and math.abs(y) < 1e100 then -- Valid point
-- Convert data coordinates to SVG coordinates
local svg_x = content_x + ((x - x_min) / (x_max - x_min)) * content_width
local svg_y = content_y + content_height - ((y - y_min) / (y_max - y_min)) * content_height
if svg_x and svg_y and svg_x >= content_x and svg_x <= content_x + content_width and
svg_y >= content_y and svg_y <= content_y + content_height then
table.insert(current_segment, tostring(svg_x) .. ',' .. tostring(svg_y))
else
-- Point outside view - start new segment
if #current_segment > 1 then
table.insert(segments, current_segment)
end
current_segment = {}
end
else
-- Invalid point (NaN or infinity) - start new segment
if #current_segment > 1 then
table.insert(segments, current_segment)
end
current_segment = {}
end
end
-- Add the last segment if valid
if #current_segment > 1 then
table.insert(segments, current_segment)
end
-- Draw each continuous segment separately
for _, segment_points in ipairs(segments) do
if #segment_points > 1 then
local polyline = mw.html.create('polyline')
:css('stroke', color)
:attr('points', table.concat(segment_points, ' '))
:attr('fill', 'none')
:css('stroke-width', '2px')
:css('stroke-linejoin', 'round')
:css('stroke-linecap', 'round')
content = content .. tostring(polyline)
end
end
end
elseif series_type == "parametric" then
-- Parametric equation - use its own coordinate system
local t_points = mw.text.split(series_data.t or '', ',')
local x_points = mw.text.split(series_data.x or '', ',')
local y_points = mw.text.split(series_data.y or '', ',')
if #t_points == #x_points and #t_points == #y_points and #t_points > 1 then
local points = {}
for j = 1, #t_points do
local x = tonumber(x_points[j])
local y = tonumber(y_points[j])
if x and y and y ~= 0/0 and math.abs(y) < 1e100 and x ~= 0/0 and math.abs(x) < 1e100 then
-- Convert parametric coordinates to SVG coordinates
local svg_x = content_x + ((x - x_min) / (x_max - x_min)) * content_width
local svg_y = content_y + content_height - ((y - y_min) / (y_max - y_min)) * content_height
if svg_x and svg_y and svg_x >= content_x and svg_x <= content_x + content_width and
svg_y >= content_y and svg_y <= content_y + content_height then
table.insert(points, tostring(svg_x) .. ',' .. tostring(svg_y))
end
end
end
if #points > 1 then
local polyline = mw.html.create('polyline')
:css('stroke', color)
:attr('points', table.concat(points, ' '))
:attr('fill', 'none')
:css('stroke-width', '2px')
:css('stroke-linejoin', 'round')
:css('stroke-linecap', 'round')
content = content .. tostring(polyline)
end
end
end
end
-- Add graph title
if graph_data.title then
local title = mw.html.create('text')
:attr('x', tostring(width / 2))
:attr('y', tostring(20))
:attr('text-anchor', 'middle')
:attr('font-family', 'sans-serif')
:attr('font-size', '14px')
:attr('font-weight', 'bold')
:attr('fill', '#333333')
:wikitext(graph_data.title)
content = content .. tostring(title)
end
-- Add X-axis title
if graph_data.x_label then
local x_title = mw.html.create('text')
:attr('x', tostring(content_x + content_width / 2))
:attr('y', tostring(height - 10))
:attr('text-anchor', 'middle')
:attr('font-family', 'sans-serif')
:attr('font-size', '12px')
:attr('fill', '#333333')
:wikitext(graph_data.x_label)
content = content .. tostring(x_title)
end
-- Add Y-axis title
if graph_data.y_label then
local y_title = mw.html.create('text')
:attr('x', tostring(10))
:attr('y', tostring(content_y + content_height / 2))
:attr('text-anchor', 'middle')
:attr('font-family', 'sans-serif')
:attr('font-size', '12px')
:attr('fill', '#333333')
:attr('transform', 'rotate(-90, 10, ' .. tostring(content_y + content_height / 2) .. ')')
:wikitext(graph_data.y_label)
content = content .. tostring(y_title)
end
-- Add legend
if #(graph_data.series or {}) > 0 then
local legend_x = content_x + content_width - 120
local legend_y = content_y + 10
local legend_bg = mw.html.create('rect')
:attr('x', tostring(legend_x - 5))
:attr('y', tostring(legend_y - 5))
:attr('width', '130')
:attr('height', tostring(20 + #graph_data.series * 20))
:attr('fill', 'white')
:attr('stroke', '#cccccc')
:attr('stroke-width', '1')
:attr('fill-opacity', '0.9')
content = content .. tostring(legend_bg)
local legend_title = mw.html.create('text')
:attr('x', tostring(legend_x))
:attr('y', tostring(legend_y + 10))
:attr('font-family', 'sans-serif')
:attr('font-size', '10px')
:attr('font-weight', 'bold')
:attr('fill', '#333333')
:wikitext('Legend')
content = content .. tostring(legend_title)
for i, series_data in ipairs(graph_data.series) do
local legend_item_y = legend_y + 15 + i * 15
-- Color indicator
local color_indicator = mw.html.create('rect')
:attr('x', tostring(legend_x))
:attr('y', tostring(legend_item_y - 6))
:attr('width', '12')
:attr('height', '3')
:attr('fill', colors[(i-1) % #colors + 1])
content = content .. tostring(color_indicator)
-- Series label
local series_label = series_data.title or 'Series ' .. tostring(i)
local legend_label = mw.html.create('text')
:attr('x', tostring(legend_x + 15))
:attr('y', tostring(legend_item_y))
:attr('font-family', 'sans-serif')
:attr('font-size', '9px')
:attr('fill', '#333333')
:wikitext(series_label)
content = content .. tostring(legend_label)
end
end
svg:setContent(content)
return svg:toImage()
end
function p.drawComplexGraphSVG(complex_data, width)
local svg = mw.svg.new()
local height = width -- Square aspect ratio for complex graphs
svg:setAttribute('viewBox', '0 0 ' .. tostring(width) .. ' ' .. tostring(height))
svg:setImgAttribute('width', tostring(width))
svg:setImgAttribute('height', tostring(height))
svg:setImgAttribute('class', 'complex-graph-svg')
local content = ''
-- Calculate padding for labels
local padding = 30
local content_size = width - 2 * padding
-- Draw background
local background = mw.html.create('rect')
:attr('x', tostring(padding))
:attr('y', tostring(padding))
:attr('width', tostring(content_size))
:attr('height', tostring(content_size))
:attr('fill', '#f8f8f8')
:attr('stroke', '#e0e0e0')
:attr('stroke-width', '1')
content = content .. tostring(background)
-- Draw complex function visualization
local pixel_size = content_size / #complex_data
for i, row in ipairs(complex_data) do
for j, color in ipairs(row) do
local rect = mw.html.create('rect')
:attr('x', tostring(padding + (j-1) * pixel_size))
:attr('y', tostring(padding + (i-1) * pixel_size))
:attr('width', tostring(pixel_size + 0.1)) -- Small overlap to avoid gaps
:attr('height', tostring(pixel_size + 0.1))
:attr('fill', color)
content = content .. tostring(rect)
end
end
-- Add title
local title = mw.html.create('text')
:attr('x', tostring(width / 2))
:attr('y', tostring(20))
:attr('text-anchor', 'middle')
:attr('font-family', 'sans-serif')
:attr('font-size', '14px')
:attr('font-weight', 'bold')
:attr('fill', '#333333')
:wikitext('Complex Function Visualization')
content = content .. tostring(title)
-- Add axis labels
local real_label = mw.html.create('text')
:attr('x', tostring(width / 2))
:attr('y', tostring(height - 10))
:attr('text-anchor', 'middle')
:attr('font-family', 'sans-serif')
:attr('font-size', '12px')
:attr('fill', '#333333')
:wikitext('Real Axis')
content = content .. tostring(real_label)
local imag_label = mw.html.create('text')
:attr('x', tostring(10))
:attr('y', tostring(height / 2))
:attr('text-anchor', 'middle')
:attr('font-family', 'sans-serif')
:attr('font-size', '12px')
:attr('fill', '#333333')
:attr('transform', 'rotate(-90, 10, ' .. tostring(height / 2) .. ')')
:wikitext('Imaginary Axis')
content = content .. tostring(imag_label)
svg:setContent(content)
return svg:toImage()
end
function p.applyFunctionGraph(frame)
local working_frame = mw.getCurrentFrame()
local graph_data = p._functionGraph({frame.args[1] or 'x'}, -10, 10, 50, nil, nil, {}, math, tonumber)
graph_data.width = 400
graph_data.height = 300
graph_data.x_min = -10
graph_data.x_max = 10
graph_data.y_min = -2
graph_data.y_max = 2
graph_data.title = 'Function Graph'
graph_data.x_label = 'x'
graph_data.y_label = 'y'
return p.drawFunctionGraphSVG(graph_data)
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
-- Parse all arguments
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 graph_data = 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] ) )
-- Add dimensions and range info
graph_data.width = body_args.width or 400
graph_data.height = body_args.height or 300
-- Use provided ranges or calculate from data
graph_data.x_min = x_start
graph_data.x_max = x_end
if y_min == nil or y_max == nil then
local calc_y_min, calc_y_max = 0, 1
for i, series_data in ipairs(graph_data.series or {}) do
local y_points = mw.text.split(series_data.y or '', ',')
for _, point in ipairs(y_points) do
local val = tonumber(point) or 0
if val < calc_y_min then calc_y_min = val end
if val > calc_y_max then calc_y_max = val end
end
end
local y_range = calc_y_max - calc_y_min
if y_range == 0 then y_range = 1 end
graph_data.y_min = y_min or (calc_y_min - y_range * 0.1)
graph_data.y_max = y_max or (calc_y_max + y_range * 0.1)
else
graph_data.y_min = y_min
graph_data.y_max = y_max
end
-- Add titles and labels
graph_data.title = body_args.title or body_args.caption or 'Function Graph'
graph_data.x_label = body_args.x_label or 'x'
graph_data.y_label = body_args.y_label or 'y'
local body = p.drawFunctionGraphSVG(graph_data)
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 result = {series = {}}
local check_func = {}
-- Process each expression
for i, expr in ipairs(expr_arr) do
local local_func_sign = '↦'
local check_parametric = mw.text.split(expr,';')
-- Check if this is a parametric equation
if mw.ustring.find(expr, local_func_sign) then
check_parametric = mw.text.split(expr,'\\;')
end
if #check_parametric == 1 then
-- Regular function
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 x_points, y_points = {}, {}
for j=0,sampling do
local it = x_start + (j * (x_end-x_start) / sampling)
local calc_val = " "
xpcall(function()
calc_val = lib_calc.calc_by_postfix(postfix, {
x=it,
last=function(num)
local last_num = (body_args or {})['last' .. tonumber(tostring(num or 1))] or 0
return numberConstructer((#y_points > 0 and y_points[#y_points-(tonumber(tostring(num))or 1)+1]) or last_num)
end,
}, mathlib, numberConstructer, false)
if( tonumber((body_args or {})["calc diff " .. tostring(i) ]) == 1 or ((yesno or require('Module:Yesno'))((body_args or {})["calc diff " .. tostring(i) ]or'no')==true) )then
local dy = lib_calc.calc_by_postfix(postfix, {x=(it + 1e-6)}, mathlib, numberConstructer, false)
calc_val = 1e6 * (dy - calc_val)
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
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 = nil
end
table.insert(x_points, tostring(it))
table.insert(y_points, tostring(calc_val or 0/0)) -- Use NaN for invalid points
end
local series_title = tostring( (body_args or {})[tostring(i) .. " name" ] or expr )
if( tonumber((body_args or {})["calc diff " .. tostring(i) ]) == 1 )then
series_title = '( ' .. series_title .. " )'"
end
if check_func[series_title] ~= nil then
local new_name = series_title .. " ,(" .. tostring(check_func[series_title]+1) .. ")"
check_func[series_title] = check_func[series_title] + 1
series_title = new_name
else
check_func[series_title] = 1
end
table.insert(result.series, {
type = "function",
x = table.concat(x_points, ','),
y = table.concat(y_points, ','),
title = series_title
})
elseif #check_parametric >= 3 then
-- Parametric equation
local x_name = check_parametric[1] or 't'
local y_name = check_parametric[2] or 't'
local t_var = mw.text.trim(check_parametric[3] or 't')
local t_min = numberConstructer(check_parametric[4]) or numberConstructer(0)
local t_max = numberConstructer(check_parametric[5]) or numberConstructer(1)
local x_expr = lib_calc.infixToPostfix(x_name, debug_flag)
local y_expr = lib_calc.infixToPostfix(y_name, debug_flag)
local t_points, x_points, y_points = {}, {}, {}
for j=0,sampling do
local t_val = t_min + (j * (t_max - t_min) / sampling)
local x_val, y_val = " ", " "
xpcall(function()
x_val = lib_calc.calc_by_postfix(x_expr, {[t_var]=t_val}, mathlib, numberConstructer, false)
y_val = lib_calc.calc_by_postfix(y_expr, {[t_var]=t_val}, mathlib, numberConstructer, false)
end, function(_) end)
local x_check = mw.ustring.lower(tostring(numberConstructer(x_val)))
local y_check = mw.ustring.lower(tostring(numberConstructer(y_val)))
if mw.ustring.match(x_check,"nan") or mw.ustring.match(x_check,"nil") or mw.ustring.match(x_check,"inf") then
x_val = nil
end
if mw.ustring.match(y_check,"nan") or mw.ustring.match(y_check,"nil") or mw.ustring.match(y_check,"inf") then
y_val = nil
end
table.insert(t_points, tostring(t_val))
table.insert(x_points, tostring(x_val or 0/0))
table.insert(y_points, tostring(y_val or 0/0))
end
local series_title = "x=" .. x_name .. "; y=" .. y_name
if check_func[series_title] ~= nil then
local new_name = series_title .. " ,(" .. tostring(check_func[series_title]+1) .. ")"
check_func[series_title] = check_func[series_title] + 1
series_title = new_name
else
check_func[series_title] = 1
end
table.insert(result.series, {
type = "parametric",
t = table.concat(t_points, ','),
x = table.concat(x_points, ','),
y = table.concat(y_points, ','),
title = series_title
})
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 = p.drawComplexGraphSVG(calc_result, width)
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
return result
end
return p