SvgBuilder::pushCommand()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
1
<?php
2
3
namespace Nielsiano\DmplBuilder;
4
5
use Exception;
6
7
/**
8
 * Illustrate a plot program visually as an SVG line drawing.
9
 */
10
class SvgBuilder implements PlotBuilder
11
{
12
    /**
13
     * Current position.
14
     */
15
    protected $x = 0;
16
    protected $y = 0;
17
18
    /**
19
     * Extent of drawing
20
     */
21
    protected $maxX = 0;
22
    protected $maxY = 0;
23
24
    protected $instructions = [];
25
26
    protected $axesFlipped = false;
27
    protected $penIsDown = true;
28
    protected $tool = 'regular';
29
    protected $unit = 'mm';
30
    protected $scale = 0.1;
31
32
    const TOOLS = [
33
        0 => 'regular',
34
        6 => 'flex',
35
    ];
36
37
    /**
38
     * Adds a new plot of x and y to machine instructions.
39
     */
40
    public function plot(int $x, int $y): PlotBuilder
41
    {
42
43
        if ($this->axesFlipped) {
44
            list($x, $y) = [$y, $x];
45
        }
46
47
        $targetX = $this->x + $x;
48
        $targetY = $this->y + $y;
49
50
        $this->maxX = max($this->maxX, $targetX);
51
        $this->maxY = max($this->maxY, $targetY);
52
53
        if ($this->penIsDown) {
54
            $this->pushInstruction('line', [
55
                'x1'    => $this->x,
56
                'y1'    => $this->y,
57
                'x2'    => $targetX,
58
                'y2'    => $targetY,
59
                'class' => $this->tool
60
            ]);
61
        }
62
63
        $this->x = $targetX;
64
        $this->y = $targetY;
65
66
        return $this;
67
    }
68
69
    /**
70
     * Changes the pen of the plotter.
71
     */
72
    public function changePen(int $pen): PlotBuilder
73
    {
74
        $this->tool = self::TOOLS[$pen];
75
76
        return $this;
77
    }
78
79
    /**
80
     * Compiles a string in target format with machine instructions.
81
     */
82
    public function compile(): string
83
    {
84
        $instructions = implode("\n", $this->instructions);
85
86
        $width = ($this->maxX * $this->scale) . $this->unit;
87
        $height = ($this->maxY * $this->scale) . $this->unit;
88
89
        return <<<SVG
90
<svg xmlns="http://www.w3.org/2000/svg" width="{$width}" height="{$height}" viewBox="0 0 {$this->maxX} {$this->maxY}">
91
    <defs>
92
        <style>
93
            line.regular {
94
                stroke: rgb(0,0,255);
95
                stroke-width: 4;
96
            }
97
98
            line.flex {
99
                stroke: rgb(255,0,0);
100
                stroke-width: 4;
101
                stroke-dasharray: 20 4;
102
            }
103
        </style>
104
    </defs>
105
    {$instructions}
106
</svg>
107
SVG;
108
    }
109
110
    /**
111
     * Pushes a command to the instructions.
112
     *
113
     * No effect in the SVG output.
114
     */
115
    public function pushCommand(string $command): PlotBuilder
116
    {
117
        return $this;
118
    }
119
120
    /**
121
     * Lifts the pen up.
122
     */
123
    public function penUp(): PlotBuilder
124
    {
125
        $this->penIsDown = false;
126
127
        return $this;
128
    }
129
130
    /**
131
     * Pushes the pen down on paper.
132
     */
133
    public function penDown(): PlotBuilder
134
    {
135
        $this->penIsDown = true;
136
137
        return $this;
138
    }
139
140
    /**
141
     * Changes the plotter pen to use flexcut.
142
     */
143
    public function flexCut(): PlotBuilder
144
    {
145
        return $this->changePen(6);
146
    }
147
148
    /**
149
     * Change to the regular plotter pen.
150
     */
151
    public function regularCut(): PlotBuilder
152
    {
153
        return $this->changePen(0);
154
    }
155
156
    /**
157
     * Changes the pen pressure in gram.
158
     *
159
     * No effect in the SVG output.
160
     */
161
    public function pressure(int $gramPressure): PlotBuilder
162
    {
163
        return $this;
164
    }
165
166
    /**
167
     * Specifies measuring unit.
168
     * 1 selects 0.001 inch
169
     * 5 selects 0.005 inch
170
     * M selects 0.1 mm
171
     *
172
     * @throws Exception
173
     */
174
    public function setMeasuringUnit($unit): PlotBuilder
175
    {
176
        switch ($unit) {
177
            case 'M':
178
                $this->unit = 'mm';
179
                $this->scale = 0.1;
180
                break;
181
            case 1:
182
                $this->unit = 'in';
183
                $this->scale = 0.001;
184
                break;
185
            case 5:
186
                $this->unit = 'in';
187
                $this->scale = 0.005;
188
                break;
189
            default:
190
                throw new Exception('Unhandled unit: ' . $unit);
191
        }
192
193
        return $this;
194
    }
195
196
    /**
197
     * Changes the plotter velocity.
198
     *
199
     * No effect in the SVG output.
200
     */
201
    public function velocity(int $velocity): PlotBuilder
202
    {
203
        return $this;
204
    }
205
206
    /**
207
     * Flips the x, y coordinates.
208
     */
209
    public function flipAxes(): PlotBuilder
210
    {
211
        $this->axesFlipped = true;
212
213
        return $this;
214
    }
215
216
    /**
217
     * Cuts off paper when a operation finishes.
218
     *
219
     * No effect in the SVG output.
220
     */
221
    public function cutOff(): PlotBuilder
222
    {
223
        return $this;
224
    }
225
226
    protected function pushInstruction(string $name, array $parameters): PlotBuilder
227
    {
228
        $instruction = '<' . $name;
229
        foreach ($parameters as $parameter => $value) {
230
            $instruction .= ' ' . $parameter . '="' . htmlspecialchars($value) . '"';
231
        }
232
        $this->instructions[] = $instruction . ' />';
233
234
        return $this;
235
    }
236
237
}
238