31        'mediumpurple1', 
'palegreen3', 
'gold1', 
'cadetblue1',
 
   32        'darkmagenta', 
'coral', 
'dodgerblue3', 
'eggplant',
 
   33        'mediumblue', 
'magenta', 
'sandybrown', 
'cyan',
 
   34        'firebrick1', 
'forestgreen', 
'deeppink4', 
'darkolivegreen',
 
   58    private static function init(): void
 
   60        static $loaded = 
false;
 
   65        \JpGraph\JpGraph::load();
 
   66        \JpGraph\JpGraph::module(
'bar');
 
   67        \JpGraph\JpGraph::module(
'contour');
 
   68        \JpGraph\JpGraph::module(
'line');
 
   69        \JpGraph\JpGraph::module(
'pie');
 
   70        \JpGraph\JpGraph::module(
'pie3d');
 
   71        \JpGraph\JpGraph::module(
'radar');
 
   72        \JpGraph\JpGraph::module(
'regstat');
 
   73        \JpGraph\JpGraph::module(
'scatter');
 
   74        \JpGraph\JpGraph::module(
'stock');
 
   77            'diamond' => MARK_DIAMOND,
 
   78            'square' => MARK_SQUARE,
 
   79            'triangle' => MARK_UTRIANGLE,
 
   82            'dot' => MARK_FILLEDCIRCLE,
 
   83            'dash' => MARK_DTRIANGLE,
 
   84            'circle' => MARK_CIRCLE,
 
   93        $plotMarkKeys = array_keys(self::$markSet);
 
   94        if ($markerID === 
null) {
 
   96            self::$plotMark %= count(self::$markSet);
 
   97            $seriesPlot->mark->SetType(self::$markSet[$plotMarkKeys[self::$plotMark++]]);
 
   98        } elseif ($markerID !== 
'none') {
 
  100            if (isset(self::$markSet[$markerID])) {
 
  101                $seriesPlot->mark->SetType(self::$markSet[$markerID]);
 
  104                self::$plotMark %= count(self::$markSet);
 
  105                $seriesPlot->mark->SetType(self::$markSet[$plotMarkKeys[self::$plotMark++]]);
 
  109            $seriesPlot->mark->Hide();
 
  111        $seriesPlot->mark->SetColor(self::$colourSet[self::$plotColour]);
 
  112        $seriesPlot->mark->SetFillColor(self::$colourSet[self::$plotColour]);
 
  113        $seriesPlot->SetColor(self::$colourSet[self::$plotColour++]);
 
  120        $datasetLabelFormatCode = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotCategoryByIndex(0)->getFormatCode();
 
  121        if ($datasetLabelFormatCode !== 
null) {
 
  123            $datasetLabelFormatCode = stripslashes($datasetLabelFormatCode);
 
  126        $testCurrentIndex = 0;
 
  127        foreach ($datasetLabels as 
$i => $datasetLabel) {
 
  128            if (is_array($datasetLabel)) {
 
  129                if ($rotation == 
'bar') {
 
  130                    $datasetLabels[
$i] = implode(
' ', $datasetLabel);
 
  132                    $datasetLabel = array_reverse($datasetLabel);
 
  133                    $datasetLabels[
$i] = implode(
"\n", $datasetLabel);
 
  137                if ($datasetLabelFormatCode !== 
null) {
 
  144        return $datasetLabels;
 
  151        for (
$i = 0; 
$i < $seriesCount; ++
$i) {
 
  153                $sumValues = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex(
$i)->getDataValues();
 
  155                $nextValues = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex(
$i)->getDataValues();
 
  156                foreach ($nextValues as $k => $value) {
 
  157                    if (isset($sumValues[$k])) {
 
  158                        $sumValues[$k] += $value;
 
  160                        $sumValues[$k] = $value;
 
  171        foreach ($dataValues as $k => $dataValue) {
 
  172            $dataValues[$k] = $dataValue / $sumValues[$k] * 100;
 
  181        $caption = ($captionElement !== 
null) ? $captionElement->getCaption() : 
null;
 
  183        if ($caption !== 
null) {
 
  185            if (is_array($caption)) {
 
  187                $caption = implode(
'', $caption);
 
  198            $this->graph->title->Set(
$title);
 
  204        $legend = $this->chart->getLegend();
 
  206            $legendPosition = 
$legend->getPosition();
 
  207            switch ($legendPosition) {
 
  209                    $this->graph->legend->SetPos(0.01, 0.5, 
'right', 
'center'); 
 
  210                    $this->graph->legend->SetColumns(1);
 
  214                    $this->graph->legend->SetPos(0.01, 0.5, 
'left', 
'center'); 
 
  215                    $this->graph->legend->SetColumns(1);
 
  219                    $this->graph->legend->SetPos(0.5, 0.01, 
'center', 
'top'); 
 
  223                    $this->graph->legend->SetPos(0.5, 0.99, 
'center', 
'bottom'); 
 
  227                    $this->graph->legend->SetPos(0.01, 0.01, 
'right', 
'top'); 
 
  228                    $this->graph->legend->SetColumns(1);
 
  233            $this->graph->legend->Hide();
 
  239        $this->graph = 
new Graph(self::$width, self::$height);
 
  240        $this->graph->SetScale(
$type);
 
  245        $rotation = $this->chart->getPlotArea()->getPlotGroupByIndex(0)->getPlotDirection();
 
  246        $reverse = $rotation == 
'bar';
 
  248        $xAxisLabel = $this->chart->getXAxisLabel();
 
  249        if ($xAxisLabel !== 
null) {
 
  252                $this->graph->xaxis->SetTitle(
$title, 
'center');
 
  253                $this->graph->xaxis->title->SetMargin(35);
 
  255                    $this->graph->xaxis->title->SetAngle(90);
 
  256                    $this->graph->xaxis->title->SetMargin(90);
 
  261        $yAxisLabel = $this->chart->getYAxisLabel();
 
  262        if ($yAxisLabel !== 
null) {
 
  265                $this->graph->yaxis->SetTitle(
$title, 
'center');
 
  267                    $this->graph->yaxis->title->SetAngle(0);
 
  268                    $this->graph->yaxis->title->SetMargin(-55);
 
  276        $this->graph = 
new PieGraph(self::$width, self::$height);
 
  283        $this->graph = 
new RadarGraph(self::$width, self::$height);
 
  284        $this->graph->SetScale(
'lin');
 
  289    private function renderPlotLine($groupID, $filled = 
false, $combination = 
false, $dimensions = 
'2d'): void
 
  291        $grouping = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotGrouping();
 
  293        $labelCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex(0)->getPointCount();
 
  294        if ($labelCount > 0) {
 
  295            $datasetLabels = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotCategoryByIndex(0)->getDataValues();
 
  297            $this->graph->xaxis->SetTickLabels($datasetLabels);
 
  300        $seriesCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotSeriesCount();
 
  302        if ($grouping == 
'percentStacked') {
 
  309        for (
$i = 0; 
$i < $seriesCount; ++
$i) {
 
  310            $dataValues = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex(
$i)->getDataValues();
 
  311            $marker = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex(
$i)->getPointMarker();
 
  313            if ($grouping == 
'percentStacked') {
 
  318            $testCurrentIndex = 0;
 
  319            foreach ($dataValues as $k => $dataValue) {
 
  320                while ($k != $testCurrentIndex) {
 
  321                    $dataValues[$testCurrentIndex] = 
null;
 
  327            $seriesPlot = 
new LinePlot($dataValues);
 
  329                $seriesPlot->SetBarCenter();
 
  333                $seriesPlot->SetFilled(
true);
 
  334                $seriesPlot->SetColor(
'black');
 
  335                $seriesPlot->SetFillColor(self::$colourSet[self::$plotColour++]);
 
  340            $dataLabel = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotLabelByIndex(
$i)->getDataValue();
 
  341            $seriesPlot->SetLegend($dataLabel);
 
  343            $seriesPlots[] = $seriesPlot;
 
  346        if ($grouping == 
'standard') {
 
  347            $groupPlot = $seriesPlots;
 
  349            $groupPlot = 
new AccLinePlot($seriesPlots);
 
  351        $this->graph->Add($groupPlot);
 
  356        $rotation = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotDirection();
 
  358        if (($groupID == 0) && ($rotation == 
'bar')) {
 
  359            $this->graph->Set90AndMargin();
 
  361        $grouping = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotGrouping();
 
  363        $labelCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex(0)->getPointCount();
 
  364        if ($labelCount > 0) {
 
  365            $datasetLabels = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotCategoryByIndex(0)->getDataValues();
 
  366            $datasetLabels = $this->
formatDataSetLabels($groupID, $datasetLabels, $labelCount, $rotation);
 
  368            if ($rotation == 
'bar') {
 
  369                $datasetLabels = array_reverse($datasetLabels);
 
  370                $this->graph->yaxis->SetPos(
'max');
 
  371                $this->graph->yaxis->SetLabelAlign(
'center', 
'top');
 
  372                $this->graph->yaxis->SetLabelSide(SIDE_RIGHT);
 
  374            $this->graph->xaxis->SetTickLabels($datasetLabels);
 
  377        $seriesCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotSeriesCount();
 
  379        if ($grouping == 
'percentStacked') {
 
  386        for ($j = 0; $j < $seriesCount; ++$j) {
 
  387            $dataValues = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($j)->getDataValues();
 
  388            if ($grouping == 
'percentStacked') {
 
  393            $testCurrentIndex = 0;
 
  394            foreach ($dataValues as $k => $dataValue) {
 
  395                while ($k != $testCurrentIndex) {
 
  396                    $dataValues[$testCurrentIndex] = 
null;
 
  403            if ($rotation == 
'bar') {
 
  404                $dataValues = array_reverse($dataValues);
 
  406            $seriesPlot = 
new BarPlot($dataValues);
 
  407            $seriesPlot->SetColor(
'black');
 
  408            $seriesPlot->SetFillColor(self::$colourSet[self::$plotColour++]);
 
  409            if ($dimensions == 
'3d') {
 
  410                $seriesPlot->SetShadow();
 
  412            if (!$this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotLabelByIndex($j)) {
 
  415                $dataLabel = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotLabelByIndex($j)->getDataValue();
 
  417            $seriesPlot->SetLegend($dataLabel);
 
  419            $seriesPlots[] = $seriesPlot;
 
  422        if (($rotation == 
'bar') && ($grouping != 
'percentStacked')) {
 
  423            $seriesPlots = array_reverse($seriesPlots);
 
  426        if ($grouping == 
'clustered') {
 
  427            $groupPlot = 
new GroupBarPlot($seriesPlots);
 
  428        } elseif ($grouping == 
'standard') {
 
  429            $groupPlot = 
new GroupBarPlot($seriesPlots);
 
  431            $groupPlot = 
new AccBarPlot($seriesPlots);
 
  432            if ($dimensions == 
'3d') {
 
  433                $groupPlot->SetShadow();
 
  437        $this->graph->Add($groupPlot);
 
  442        $grouping = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotGrouping();
 
  443        $scatterStyle = $bubbleSize = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotStyle();
 
  445        $seriesCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotSeriesCount();
 
  449        for (
$i = 0; 
$i < $seriesCount; ++
$i) {
 
  450            $dataValuesY = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotCategoryByIndex(
$i)->getDataValues();
 
  451            $dataValuesX = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex(
$i)->getDataValues();
 
  453            foreach ($dataValuesY as $k => $dataValueY) {
 
  454                $dataValuesY[$k] = $k;
 
  457            $seriesPlot = 
new ScatterPlot($dataValuesX, $dataValuesY);
 
  458            if ($scatterStyle == 
'lineMarker') {
 
  459                $seriesPlot->SetLinkPoints();
 
  460                $seriesPlot->link->SetColor(self::$colourSet[self::$plotColour]);
 
  461            } elseif ($scatterStyle == 
'smoothMarker') {
 
  462                $spline = 
new Spline($dataValuesY, $dataValuesX);
 
  463                [$splineDataY, $splineDataX] = $spline->Get(count($dataValuesX) * self::$width / 20);
 
  464                $lplot = 
new LinePlot($splineDataX, $splineDataY);
 
  465                $lplot->SetColor(self::$colourSet[self::$plotColour]);
 
  467                $this->graph->Add($lplot);
 
  472                $seriesPlot->mark->SetColor(
'black');
 
  473                $seriesPlot->mark->SetSize($bubbleSize);
 
  475                $marker = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex(
$i)->getPointMarker();
 
  478            $dataLabel = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotLabelByIndex(
$i)->getDataValue();
 
  479            $seriesPlot->SetLegend($dataLabel);
 
  481            $this->graph->Add($seriesPlot);
 
  487        $radarStyle = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotStyle();
 
  489        $seriesCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotSeriesCount();
 
  493        for (
$i = 0; 
$i < $seriesCount; ++
$i) {
 
  494            $dataValuesY = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotCategoryByIndex(
$i)->getDataValues();
 
  495            $dataValuesX = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex(
$i)->getDataValues();
 
  496            $marker = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex(
$i)->getPointMarker();
 
  499            foreach ($dataValuesY as $k => $dataValueY) {
 
  500                $dataValues[$k] = implode(
' ', array_reverse($dataValueY));
 
  502            $tmp = array_shift($dataValues);
 
  503            $dataValues[] = $tmp;
 
  504            $tmp = array_shift($dataValuesX);
 
  505            $dataValuesX[] = $tmp;
 
  507            $this->graph->SetTitles(array_reverse($dataValues));
 
  509            $seriesPlot = 
new RadarPlot(array_reverse($dataValuesX));
 
  511            $dataLabel = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotLabelByIndex(
$i)->getDataValue();
 
  512            $seriesPlot->SetColor(self::$colourSet[self::$plotColour++]);
 
  513            if ($radarStyle == 
'filled') {
 
  514                $seriesPlot->SetFillColor(self::$colourSet[self::$plotColour]);
 
  517            $seriesPlot->SetLegend($dataLabel);
 
  519            $this->graph->Add($seriesPlot);
 
  525        $contourStyle = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotStyle();
 
  527        $seriesCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotSeriesCount();
 
  532        for (
$i = 0; 
$i < $seriesCount; ++
$i) {
 
  533            $dataValuesY = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotCategoryByIndex(
$i)->getDataValues();
 
  534            $dataValuesX = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex(
$i)->getDataValues();
 
  536            $dataValues[
$i] = $dataValuesX;
 
  538        $seriesPlot = 
new ContourPlot($dataValues);
 
  540        $this->graph->Add($seriesPlot);
 
  545        $seriesCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotSeriesCount();
 
  546        $plotOrder = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotOrder();
 
  550        foreach ($plotOrder as 
$i => $v) {
 
  551            $dataValuesX = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($v)->getDataValues();
 
  552            foreach ($dataValuesX as $j => $dataValueX) {
 
  553                $dataValues[$plotOrder[
$i]][$j] = $dataValueX;
 
  556        if (empty($dataValues)) {
 
  560        $dataValuesPlot = [];
 
  562        $jMax = count($dataValues[0]);
 
  563        for ($j = 0; $j < $jMax; ++$j) {
 
  564            for (
$i = 0; 
$i < $seriesCount; ++
$i) {
 
  565                $dataValuesPlot[] = $dataValues[
$i][$j];
 
  570        $labelCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex(0)->getPointCount();
 
  571        if ($labelCount > 0) {
 
  572            $datasetLabels = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotCategoryByIndex(0)->getDataValues();
 
  574            $this->graph->xaxis->SetTickLabels($datasetLabels);
 
  577        $seriesPlot = 
new StockPlot($dataValuesPlot);
 
  578        $seriesPlot->SetWidth(20);
 
  580        $this->graph->Add($seriesPlot);
 
  587        for (
$i = 0; 
$i < $groupCount; ++
$i) {
 
  596        for (
$i = 0; 
$i < $groupCount; ++
$i) {
 
  605        for (
$i = 0; 
$i < $groupCount; ++
$i) {
 
  614        for (
$i = 0; 
$i < $groupCount; ++
$i) {
 
  623        for (
$i = 0; 
$i < $groupCount; ++
$i) {
 
  628    private function renderPieChart($groupCount, $dimensions = 
'2d', $doughnut = 
false, $multiplePlots = 
false): void
 
  632        $iLimit = ($multiplePlots) ? $groupCount : 1;
 
  633        for ($groupID = 0; $groupID < $iLimit; ++$groupID) {
 
  634            $grouping = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotGrouping();
 
  635            $exploded = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotStyle();
 
  638                $labelCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex(0)->getPointCount();
 
  639                if ($labelCount > 0) {
 
  640                    $datasetLabels = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotCategoryByIndex(0)->getDataValues();
 
  645            $seriesCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotSeriesCount();
 
  648            $jLimit = ($multiplePlots) ? $seriesCount : 1;
 
  650            for ($j = 0; $j < $jLimit; ++$j) {
 
  651                $dataValues = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($j)->getDataValues();
 
  654                $testCurrentIndex = 0;
 
  655                foreach ($dataValues as $k => $dataValue) {
 
  656                    while ($k != $testCurrentIndex) {
 
  657                        $dataValues[$testCurrentIndex] = 
null;
 
  663                if ($dimensions == 
'3d') {
 
  664                    $seriesPlot = 
new PiePlot3D($dataValues);
 
  667                        $seriesPlot = 
new PiePlotC($dataValues);
 
  669                        $seriesPlot = 
new PiePlot($dataValues);
 
  673                if ($multiplePlots) {
 
  674                    $seriesPlot->SetSize(($jLimit - $j) / ($jLimit * 4));
 
  678                    $seriesPlot->SetMidColor(
'white');
 
  681                $seriesPlot->SetColor(self::$colourSet[self::$plotColour++]);
 
  682                if (count($datasetLabels) > 0) {
 
  683                    $seriesPlot->SetLabels(array_fill(0, count($datasetLabels), 
''));
 
  685                if ($dimensions != 
'3d') {
 
  686                    $seriesPlot->SetGuideLines(
false);
 
  690                        $seriesPlot->ExplodeAll();
 
  692                    $seriesPlot->SetLegends($datasetLabels);
 
  695                $this->graph->Add($seriesPlot);
 
  704        for ($groupID = 0; $groupID < $groupCount; ++$groupID) {
 
  713        for ($groupID = 0; $groupID < $groupCount; ++$groupID) {
 
  722        for (
$i = 0; 
$i < $groupCount; ++
$i) {
 
  731        for (
$i = 0; 
$i < $groupCount; ++
$i) {
 
  733            $chartType = $this->chart->getPlotArea()->getPlotGroupByIndex(
$i)->getPlotType();
 
  734            switch ($chartType) {
 
  773        $this->graph->Stroke($outputDestination);
 
  778    public function render($outputDestination)
 
  780        self::$plotColour = 0;
 
  782        $groupCount = $this->chart->getPlotArea()->getPlotGroupCount();
 
  785        if ($groupCount == 1) {
 
  786            $chartType = $this->chart->getPlotArea()->getPlotGroupByIndex(0)->getPlotType();
 
  789            for (
$i = 0; 
$i < $groupCount; ++
$i) {
 
  790                $chartTypes[] = $this->chart->getPlotArea()->getPlotGroupByIndex(
$i)->getPlotType();
 
  792            $chartTypes = array_unique($chartTypes);
 
  793            if (count($chartTypes) == 1) {
 
  794                $chartType = array_pop($chartTypes);
 
  795            } elseif (count($chartTypes) == 0) {
 
  796                echo 
'Chart is not yet implemented<br />';
 
  804        switch ($chartType) {
 
  833            case 'doughnut3DChart':
 
  836            case 'doughnutChart':
 
  852            case 'surface3DChart':
 
  864                echo $chartType . 
' is not yet implemented<br />';
 
  870        $this->graph->Stroke($outputDestination);
 
An exception for terminatinating execution or to throw for unit testing.
renderRadarChart($groupCount)
__construct(Chart $chart)
Create a new jpgraph.
percentageSumCalculation($groupID, $seriesCount)
render($outputDestination)
Render the chart to given file (or stream).
renderPlotContour($groupID)
renderPlotStock($groupID)
percentageAdjustValues($dataValues, $sumValues)
renderPieChart($groupCount, $dimensions='2d', $doughnut=false, $multiplePlots=false)
renderPlotBar($groupID, $dimensions='2d')
renderStockChart($groupCount)
getCaption($captionElement)
formatPointMarker($seriesPlot, $markerID)
renderCombinationChart($groupCount, $dimensions, $outputDestination)
renderPlotRadar($groupID)
renderLineChart($groupCount, $dimensions='2d')
renderContourChart($groupCount, $dimensions)
renderScatterChart($groupCount)
renderAreaChart($groupCount, $dimensions='2d')
renderBubbleChart($groupCount)
renderCartesianPlotArea($type='textlin')
formatDataSetLabels($groupID, $datasetLabels, $labelCount, $rotation='')
renderPlotScatter($groupID, $bubble)
renderBarChart($groupCount, $dimensions='2d')
renderPlotLine($groupID, $filled=false, $combination=false, $dimensions='2d')