Skip to content

Commit

Permalink
Add step line (#13)
Browse files Browse the repository at this point in the history
  • Loading branch information
maantje authored Jan 19, 2025
1 parent 195d9de commit 0b87455
Show file tree
Hide file tree
Showing 6 changed files with 347 additions and 2 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ Below are some examples of the types of charts you can create using this library
![alt text](./examples/output/simple-curved-chart.svg)
[View source](./examples/simple-curved-chart.php)

### Step line chart
![alt text](./examples/output/simple-step-chart.svg)
[View source](./examples/simple-step-chart.php)

### Simple bar chart
![alt text](./examples/output/simple-bar-chart.svg)
[View source](./examples/simple-bar-chart.php)
Expand Down
20 changes: 20 additions & 0 deletions examples/output/simple-step-chart.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
36 changes: 36 additions & 0 deletions examples/simple-step-chart.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

use Maantje\Charts\Chart;
use Maantje\Charts\Line\Line;
use Maantje\Charts\Line\Lines;
use Maantje\Charts\Line\Point;

require '../vendor/autoload.php';

$chart = new Chart(
series: [
new Lines(
lines: [
new Line(
points: [
new Point(y: 15, x: 0),
new Point(y: 25, x: 50),
new Point(y: 20, x: 100),
new Point(y: 45, x: 150),
new Point(y: 35, x: 200),
new Point(y: 65, x: 250),
new Point(y: 55, x: 300),
new Point(y: 85, x: 350),
new Point(y: 75, x: 400),
new Point(y: 110, x: 450),
],
size: 4,
lineColor: 'purple',
stepLine: true
),
]
),
],
);

echo $chart->render();
29 changes: 27 additions & 2 deletions src/Line/Line.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ public function __construct(
public int $size = 5,
public ?string $yAxis = null,
public string $lineColor = 'black',
public ?float $curve = null
public ?float $curve = null,
public bool $stepLine = false,
) {}

public function render(Chart $chart): string
Expand All @@ -36,7 +37,9 @@ public function render(Chart $chart): string
$pointsSvg .= $point->render($x, $y);
}

$d = $this->generateSmoothPath($points, $this->curve);
$d = $this->stepLine
? $this->generateStepPath($points)
: $this->generateSmoothPath($points, $this->curve);

return new Fragment([
new Path(
Expand Down Expand Up @@ -90,4 +93,26 @@ public function generateSmoothPath(array $points, ?float $curveFactor = null): s

return $d;
}

/**
* @param array{float, float}[] $points
*/
public function generateStepPath(array $points): string
{
if (count($points) < 2) {
return '';
}

$d = "M {$points[0][0]},{$points[0][1]}";

for ($i = 1; $i < count($points); $i++) {
$current = $points[$i];

$d .= " H {$current[0]}";

$d .= " V {$current[1]}";
}

return $d;
}
}
130 changes: 130 additions & 0 deletions tests/Unit/CurvedLineChartTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@

<?php

use Maantje\Charts\Chart;
use Maantje\Charts\Line\Line;
use Maantje\Charts\Line\Lines;
use Maantje\Charts\Line\Point;

it('renders curved line chart', function () {
$chart = new Chart(
series: [
new Lines(
lines: [
new Line(
points: [
new Point(y: 0, x: 0),
new Point(y: 4, x: 100),
new Point(y: 12, x: 200),
new Point(y: 8, x: 300),
],
curve: 10,
),
new Line(
points: [
new Point(y: 4, x: 0),
new Point(y: 12, x: 100),
new Point(y: 24, x: 200),
new Point(y: 7, x: 300),
],
lineColor: 'blue',
curve: 10,
),
]
),
],
);

expect(pretty($chart->render()))->toBe(<<<'SVG'
<svg xmlns="http://www.w3.org/2000/svg" width="800" height="600">
<rect x="0" y="0" width="800" height="600" fill="white" fill-opacity="1" stroke="none" stroke-width="0" rx="0" ry="0">
<title/>
</rect>
<text x="45" y="555" font-family="arial" font-size="14" fill="black" stroke="none" stroke-width="0" text-anchor="end" dominant-baseline="alphabetic" alignment-baseline="auto">0</text>
<text x="45" y="450" font-family="arial" font-size="14" fill="black" stroke="none" stroke-width="0" text-anchor="end" dominant-baseline="alphabetic" alignment-baseline="auto">5</text>
<text x="45" y="345" font-family="arial" font-size="14" fill="black" stroke="none" stroke-width="0" text-anchor="end" dominant-baseline="alphabetic" alignment-baseline="auto">10</text>
<text x="45" y="240" font-family="arial" font-size="14" fill="black" stroke="none" stroke-width="0" text-anchor="end" dominant-baseline="alphabetic" alignment-baseline="auto">14</text>
<text x="45" y="135" font-family="arial" font-size="14" fill="black" stroke="none" stroke-width="0" text-anchor="end" dominant-baseline="alphabetic" alignment-baseline="auto">19</text>
<text x="45" y="30" font-family="arial" font-size="14" fill="black" stroke="none" stroke-width="0" text-anchor="end" dominant-baseline="alphabetic" alignment-baseline="auto">24</text>
<text x="20" y="262.5" font-family="arial" font-size="14" fill="black" stroke="none" stroke-width="0" text-anchor="middle" dominant-baseline="alphabetic" alignment-baseline="middle" transform="rotate(270, 20, 262.5)"/>
<line x1="55" y1="550" x2="770" y2="550" stroke="black" stroke-dasharray="none" stroke-width="1"/>
<text x="55" y="575" font-family="arial" font-size="14" fill="black" stroke="none" stroke-width="0" text-anchor="middle" dominant-baseline="alphabetic" alignment-baseline="auto">0</text>
<line x1="55" y1="550" x2="55" y2="545" stroke="black" stroke-dasharray="none" stroke-width="1"/>
<text x="293.33333333333" y="575" font-family="arial" font-size="14" fill="black" stroke="none" stroke-width="0" text-anchor="middle" dominant-baseline="alphabetic" alignment-baseline="auto">100</text>
<line x1="293.33333333333" y1="550" x2="293.33333333333" y2="545" stroke="black" stroke-dasharray="none" stroke-width="1"/>
<text x="531.66666666667" y="575" font-family="arial" font-size="14" fill="black" stroke="none" stroke-width="0" text-anchor="middle" dominant-baseline="alphabetic" alignment-baseline="auto">200</text>
<line x1="531.66666666667" y1="550" x2="531.66666666667" y2="545" stroke="black" stroke-dasharray="none" stroke-width="1"/>
<text x="770" y="575" font-family="arial" font-size="14" fill="black" stroke="none" stroke-width="0" text-anchor="middle" dominant-baseline="alphabetic" alignment-baseline="auto">300</text>
<line x1="770" y1="550" x2="770" y2="545" stroke="black" stroke-dasharray="none" stroke-width="1"/>
<text x="412.5" y="590" font-family="arial" font-size="14" fill="black" stroke="none" stroke-width="0" text-anchor="middle" dominant-baseline="alphabetic" alignment-baseline="auto"/>
<line x1="55" y1="550" x2="770" y2="550" stroke="#ccc" stroke-dasharray="none" stroke-width="1"/>
<line x1="55" y1="445" x2="770" y2="445" stroke="#ccc" stroke-dasharray="none" stroke-width="1"/>
<line x1="55" y1="340" x2="770" y2="340" stroke="#ccc" stroke-dasharray="none" stroke-width="1"/>
<line x1="55" y1="235" x2="770" y2="235" stroke="#ccc" stroke-dasharray="none" stroke-width="1"/>
<line x1="55" y1="130" x2="770" y2="130" stroke="#ccc" stroke-dasharray="none" stroke-width="1"/>
<line x1="55" y1="25" x2="770" y2="25" stroke="#ccc" stroke-dasharray="none" stroke-width="1"/>
<path d="M 55,550 C 78.83,541.25 245.67,488.75 293.33,462.50 C 341.00,436.25 484.00,296.25 531.67,287.50 C 579.33,278.75 746.17,366.25 770.00,375.00" fill="none" stroke="black" stroke-width="5"/>
<circle cx="55" cy="550" r="10" fill="rgba(0, 0, 0, 0)" stroke="none" stroke-width="0">
<title>0</title>
</circle>
<circle cx="293.33333333333" cy="462.5" r="10" fill="rgba(0, 0, 0, 0)" stroke="none" stroke-width="0">
<title>4</title>
</circle>
<circle cx="531.66666666667" cy="287.5" r="10" fill="rgba(0, 0, 0, 0)" stroke="none" stroke-width="0">
<title>12</title>
</circle>
<circle cx="770" cy="375" r="10" fill="rgba(0, 0, 0, 0)" stroke="none" stroke-width="0">
<title>8</title>
</circle>
<path d="M 55,462.5 C 78.83,445.00 245.67,331.25 293.33,287.50 C 341.00,243.75 484.00,14.06 531.67,25.00 C 579.33,35.94 746.17,359.69 770.00,396.88" fill="none" stroke="blue" stroke-width="5"/>
<circle cx="55" cy="462.5" r="10" fill="rgba(0, 0, 0, 0)" stroke="none" stroke-width="0">
<title>4</title>
</circle>
<circle cx="293.33333333333" cy="287.5" r="10" fill="rgba(0, 0, 0, 0)" stroke="none" stroke-width="0">
<title>12</title>
</circle>
<circle cx="531.66666666667" cy="25" r="10" fill="rgba(0, 0, 0, 0)" stroke="none" stroke-width="0">
<title>24</title>
</circle>
<circle cx="770" cy="396.875" r="10" fill="rgba(0, 0, 0, 0)" stroke="none" stroke-width="0">
<title>7</title>
</circle>
</svg>
SVG
);
});

it('renders empty line chart', function () {
$chart = new Chart(
series: [
new Lines(
lines: []
),
],
);

$svg = <<<'SVG'
<svg xmlns="http://www.w3.org/2000/svg" width="800" height="600">
<rect x="0" y="0" width="800" height="600" fill="white" fill-opacity="1" stroke="none" stroke-width="0" rx="0" ry="0">
<title/>
</rect>
<text x="40" y="555" font-family="arial" font-size="14" fill="black" stroke="none" stroke-width="0" text-anchor="end" dominant-baseline="alphabetic" alignment-baseline="auto">0</text>
<text x="40" y="450" font-family="arial" font-size="14" fill="black" stroke="none" stroke-width="0" text-anchor="end" dominant-baseline="alphabetic" alignment-baseline="auto">0</text>
<text x="40" y="345" font-family="arial" font-size="14" fill="black" stroke="none" stroke-width="0" text-anchor="end" dominant-baseline="alphabetic" alignment-baseline="auto">0</text>
<text x="40" y="240" font-family="arial" font-size="14" fill="black" stroke="none" stroke-width="0" text-anchor="end" dominant-baseline="alphabetic" alignment-baseline="auto">0</text>
<text x="40" y="135" font-family="arial" font-size="14" fill="black" stroke="none" stroke-width="0" text-anchor="end" dominant-baseline="alphabetic" alignment-baseline="auto">0</text>
<text x="40" y="30" font-family="arial" font-size="14" fill="black" stroke="none" stroke-width="0" text-anchor="end" dominant-baseline="alphabetic" alignment-baseline="auto">0</text>
<text x="20" y="262.5" font-family="arial" font-size="14" fill="black" stroke="none" stroke-width="0" text-anchor="middle" dominant-baseline="alphabetic" alignment-baseline="middle" transform="rotate(270, 20, 262.5)"/>
<line x1="50" y1="550" x2="770" y2="550" stroke="black" stroke-dasharray="none" stroke-width="1"/>
<text x="410" y="590" font-family="arial" font-size="14" fill="black" stroke="none" stroke-width="0" text-anchor="middle" dominant-baseline="alphabetic" alignment-baseline="auto"/>
<line x1="50" y1="550" x2="770" y2="550" stroke="#ccc" stroke-dasharray="none" stroke-width="1"/>
<line x1="50" y1="445" x2="770" y2="445" stroke="#ccc" stroke-dasharray="none" stroke-width="1"/>
<line x1="50" y1="340" x2="770" y2="340" stroke="#ccc" stroke-dasharray="none" stroke-width="1"/>
<line x1="50" y1="235" x2="770" y2="235" stroke="#ccc" stroke-dasharray="none" stroke-width="1"/>
<line x1="50" y1="130" x2="770" y2="130" stroke="#ccc" stroke-dasharray="none" stroke-width="1"/>
<line x1="50" y1="25" x2="770" y2="25" stroke="#ccc" stroke-dasharray="none" stroke-width="1"/>
</svg>
SVG;

expect(pretty($chart->render()))->toBe($svg);
});
Loading

0 comments on commit 0b87455

Please sign in to comment.