Sk.jsplotlib = Sk.jsplotlib || {}; // Skulpt translation let $builtinmodule = function (name) { let mod = {__name__: "matplotlib.pyplot"}; const STRING_COLOR = new Sk.builtin.str("color"); const STRING_COLORS = new Sk.builtin.str("colors"); const STRING_DATA = new Sk.builtin.str("data"); const STRING_LABEL = new Sk.builtin.str("label"); const STRING_ALPHA = new Sk.builtin.str("alpha"); const STRING_DASH_CAPSTYLE = new Sk.builtin.str("dash_capstyle"); const STRING_DASH_JOINSTYLE = new Sk.builtin.str("dash_joinstyle"); const STRING_FILLSTYLE = new Sk.builtin.str("fillstyle"); const STRING_LINEWIDTH = new Sk.builtin.str("linewidth"); const STRING_MARKER = new Sk.builtin.str("marker"); const STRING_LINESTYLE = new Sk.builtin.str("linestyle"); const STRING_LINESTYLES = new Sk.builtin.str("linestyles"); const STRING_BINS = new Sk.builtin.str("bins"); const STRING_ALIGN = new Sk.builtin.str("align"); const STRING_DOT_LIMIT = new Sk.builtin.str("dot_limit"); const STRING_S = new Sk.builtin.str("s"); const STRING_C = new Sk.builtin.str("c"); const STRING_LINEWIDTHS = new Sk.builtin.str("linewidths"); const STRING_EDGECOLORS = new Sk.builtin.str("edgecolors"); const STRING_WIDTH = new Sk.builtin.str("width"); const STRING_TICK_LABEL = new Sk.builtin.str("tick_label"); const STRING_ROTATION = new Sk.builtin.str("rotation"); const DEFAULT_PLOT_PADDING = .5; /*if (Sk.MatPlotLibGraphics === undefined) { throw new Sk.builtin.NameError("Can not resolve drawing area. Sk.MatPlotLibGraphics is undefined!"); }*/ function getConsole() { return Sk.MatPlotLibGraphics.target; } // 重写获取画布宽度方法 function getWidth() { return Sk.MatPlotLibGraphics.width; } // 重写获取画布长度方法 function getHeight() { return Sk.MatPlotLibGraphics.height; } // Unique ID generator for charts let chartCounter = 0; function makePlot(type, data, style, label) { return { type: type, data: data, style: style, label: label }; } function makeChart() { let margin = {top: 20, right: 30, bottom: 50, left: 40}; let chartIdNumber = chartCounter++; return { plots: [], labels: { title: "", "x-axis": "", "y-axis": "" }, extents: { xMin: null, xMax: null, yMin: null, yMax: null }, ticks: { x: {}, y: {}, xRotate: "horizontal", yRotate: "horizontal", xEstimate: new Set(), yEstimate: new Set() }, margin: margin, width: getWidth() - margin.left - margin.right, height: getWidth() - margin.top - margin.bottom, idNumber: chartIdNumber, id: "chart" + chartIdNumber, colorCycle: 0 }; } function updateExtent(chart, attr, array, padding) { if (padding === undefined) { padding = 0; } if (chart.extents[attr + "Min"] === null) { chart.extents[attr + "Min"] = d3.min(array)-padding; } else { chart.extents[attr + "Min"] = Math.min(d3.min(array)-padding, chart.extents[attr + "Min"]); } if (chart.extents[attr + "Max"] === null) { chart.extents[attr + "Max"] = d3.max(array)+padding; } else { chart.extents[attr + "Max"] = Math.max(d3.max(array)+padding, chart.extents[attr + "Max"]); } } let chart = null; function getChart() { if (!chart) { chart = makeChart(); } return chart; } function resetChart() { chart = makeChart(); } function parseFormat(styleString, chart, kwargs, defaultLinestyle, defaultMarker) { // TODO: Handle ls, lw, and other abbreviated keyword parameters let format = { alpha: getKeywordParameter(kwargs, STRING_ALPHA, null), dash_capstyle: getKeywordParameter(kwargs, STRING_DASH_CAPSTYLE, "butt"), dash_joinstyle: getKeywordParameter(kwargs, STRING_DASH_JOINSTYLE, "miter"), fillstyle: getKeywordParameter(kwargs, STRING_FILLSTYLE, "full"), linewidth: getKeywordParameter(kwargs, STRING_LINEWIDTH, 1), }; if (styleString) { let ftmTuple = Sk.jsplotlib._process_plot_format(styleString); format["linestyle"] = getKeywordParameter(kwargs, STRING_LINESTYLE, ftmTuple.linestyle); format["marker"] = getKeywordParameter(kwargs, STRING_MARKER, Sk.jsplotlib.parse_marker(ftmTuple.marker)); format["color"] = getKeywordParameter(kwargs, STRING_COLOR, Sk.jsplotlib.color_to_hex(ftmTuple.color)); } else { let cycle = Sk.jsplotlib.rc["axes.color_cycle"]; format["linestyle"] = getKeywordParameter(kwargs, STRING_LINESTYLE, defaultLinestyle); format["marker"] = getKeywordParameter(kwargs, STRING_MARKER, defaultMarker); format["color"] = getKeywordParameter(kwargs, STRING_COLOR, Sk.jsplotlib.color_to_hex(cycle[chart.colorCycle % cycle.length])); chart.colorCycle += 1; } return format; } function getIndices(values) { return values.map((value, index) => index); } function chompPlotArgs(args, data) { if (data !== null) { if (args.length >= 2) { throw new Sk.builtin.ValueError("Must provide at least 2 arguments when plotting with 'data' keyword"); } let xAttr = args[0]; let yAttr = args[1]; let xs = [], ys = []; const values = Sk.misceval.arrayFromIterable(data); for (let i = 0; i < values.length; i++) { if (values[i].sq$contains(xAttr)) { xs.push(values[i].mp$subscript(xAttr)); ys.push(values[i].mp$subscript(yAttr)); } else { throw new Sk.builtin.ValueError(`Item at index ${i} is missing expected attribute.`); } } return [xs, ys, args[2]]; } else { // x, y, style if (args.length >= 3) { if (Sk.builtin.checkString(args[2])) { return [[args[0], args[1], args[2]].map(Sk.ffi.remapToJs)].concat(chompPlotArgs(args.slice(3), data)); } } if (args.length >= 2) { let ys = Sk.ffi.remapToJs(args[0]); if (Sk.builtin.checkString(args[1])) { // y, style let xs = getIndices(ys); let style = Sk.ffi.remapToJs(args[1]); return [[xs, ys, style]].concat(chompPlotArgs(args.slice(2), data)); } else { // x, y let xs = ys; ys = Sk.ffi.remapToJs(args[1]); return [[xs, ys, ""]].concat(chompPlotArgs(args.slice(2), data)); } } if (args.length >= 1) { // y let ys = Sk.ffi.remapToJs(args[0]); let xs = getIndices(ys); return [[xs, ys, ""]].concat(chompPlotArgs(args.slice(1), data)); } return []; } } function getKeywordParameter(kwargs, key, otherwise) { if (kwargs.sq$contains(key)) { return Sk.ffi.remapToJs(kwargs.mp$subscript(key)); } else { return otherwise; } } function updateTickEstimates(chart, xValues, yValues) { xValues.forEach(value => chart.ticks.xEstimate.add(value)); yValues.forEach(value => chart.ticks.yEstimate.add(value)); } // Main plotting function let plot_f = function (kwa) { // Parse arguments Sk.builtin.pyCheckArgs("plot", arguments, 1, Infinity, true, false); let args = Array.prototype.slice.call(arguments, 1); let kwargs = new Sk.builtins.dict(kwa); // Get argument as object let data = getKeywordParameter(kwargs, STRING_DATA, null); let label = getKeywordParameter(kwargs, STRING_LABEL, null); let chart = getChart(); let plotData = chompPlotArgs(args, data); for (let i=0; i {return {x: value[0], y: value[1]};}); let style = parseFormat(plotDatum[2], chart, kwargs, "-", ""); let plot = makePlot("line", zippedData, style, label); chart.plots.push(plot); updateExtent(chart, "x", plotDatum[0], DEFAULT_PLOT_PADDING); updateExtent(chart, "y", plotDatum[1], DEFAULT_PLOT_PADDING); updateTickEstimates(chart, plotDatum[0], plotDatum[1]); } }; plot_f.co_kwargs = true; mod.plot = new Sk.builtin.func(plot_f); let hist_f = function (kwa) { // Parse arguments Sk.builtin.pyCheckArgs("hist", arguments, 1, Infinity, true, false); let args = Array.prototype.slice.call(arguments, 1); let kwargs = new Sk.builtins.dict(kwa); // Get argument as object // Parse different args combinations let plotData = Sk.ffi.remapToJs(args[0]); if (plotData.length > 0) { if (!Array.isArray(plotData[0])) { plotData = [plotData]; } } let label = getKeywordParameter(kwargs, STRING_LABEL, null); let chart = getChart(); for (let i=0; i Math.max(Math.min(value, max), min)); estimatedBins = plot.bins; } chart.plots.push(plot); updateExtent(chart, "x", plotDatum, DEFAULT_PLOT_PADDING); updateTickEstimates(chart, estimatedBins, d3.range(plotDatum.length)); } // Ensure that the axis line is always the middle! updateExtent(chart, "y", [0]); }; hist_f.co_kwargs = true; mod.hist = new Sk.builtin.func(hist_f); let scatter_f = function (kwa) { // Parse arguments Sk.builtin.pyCheckArgs("scatter", arguments, 1, Infinity, true, false); let args = Array.prototype.slice.call(arguments, 1); let kwargs = new Sk.builtins.dict(kwa); // Get argument as object let data = getKeywordParameter(kwargs, STRING_DATA, null); let label = getKeywordParameter(kwargs, STRING_LABEL, null); let plotData = chompPlotArgs(args, data); // Special dot_limit parameter to prevent crashing from too many dots in browser let dotLimit = getKeywordParameter(kwargs, STRING_DOT_LIMIT, 256); if (plotData[0] && plotData[0].length > dotLimit) { let xSampled = [], ySampled = []; let LENGTH = plotData[0].length, RATE = LENGTH / dotLimit; for (let i = 0; i < dotLimit; i += 1) { let index = Math.floor((i + Math.random()) * RATE); xSampled.push(plotData[0][index]); ySampled.push(plotData[1][index]); } plotData[0] = xSampled; plotData[1] = ySampled; } let chart = getChart(); for (let i=0; i {return {x: value[0], y: value[1]};}); let plot = makePlot("scatter", zippedData, style, label); plot.sizes = getKeywordParameter(kwargs, STRING_S, null); plot.colors = getKeywordParameter(kwargs, STRING_C, null); plot.linewidths = getKeywordParameter(kwargs, STRING_LINEWIDTHS, 1.5); plot.edgecolors = getKeywordParameter(kwargs, STRING_EDGECOLORS, "face"); chart.plots.push(plot); updateExtent(chart, "x", plotDatum[0], DEFAULT_PLOT_PADDING); updateExtent(chart, "y", plotDatum[1], DEFAULT_PLOT_PADDING); updateTickEstimates(chart, plotDatum[0], plotDatum[1]); } }; scatter_f.co_kwargs = true; mod.scatter = new Sk.builtin.func(scatter_f); let bar_f = function (kwa) { // Parse arguments Sk.builtin.pyCheckArgs("bar", arguments, 1, Infinity, true, false); let args = Array.prototype.slice.call(arguments, 1); let kwargs = new Sk.builtins.dict(kwa); // Get argument as object let data = getKeywordParameter(kwargs, STRING_DATA, null); let label = getKeywordParameter(kwargs, STRING_LABEL, null); let tickLabels = getKeywordParameter(kwargs, STRING_TICK_LABEL, []); let plotData = chompPlotArgs(args, data); let chart = getChart(); for (let i=0; i {return {x: value[0], y: value[1]};}); let style = parseFormat(null, chart, kwargs, "", ""); let plot = makePlot("bar", zippedData, style, label); plot.width = getKeywordParameter(kwargs, STRING_WIDTH, 0.8); plot.align = getKeywordParameter(kwargs, STRING_ALIGN, "center"); for (let x=0; x a.length)) || 1; args = args.map(a => Array.isArray(a) ? a : Array(length).fill(a)); return d3.zip(args[0], args[1], args[2]); } let hlines_f = function (kwa) { // Parse arguments Sk.builtin.pyCheckArgs("hlines", arguments, 1, Infinity, true, false); let args = Array.prototype.slice.call(arguments, 1); let kwargs = new Sk.builtins.dict(kwa); // Get argument as object let data = getKeywordParameter(kwargs, STRING_DATA, null); let label = getKeywordParameter(kwargs, STRING_LABEL, null); let colors = getKeywordParameter(kwargs, STRING_COLORS, null); let linestyles = getKeywordParameter(kwargs, STRING_LINESTYLES, null); let plotData = getLinesData(args); let chart = getChart(); let style = parseFormat(null, chart, kwargs, "", ""); for (let i=0; i