@@ -91,9 +91,97 @@ function draw(event) {
9191 currentPath .value += ` ${ x} ${ y} ` ;
9292}
9393
94+ function smoothPath (path ) {
95+ const segments = path .trim ().split (/ \s + / );
96+ if (segments .length < 4 ) return path;
97+ const smoothedPath = [segments[0 ], segments[1 ], segments[2 ]]; // Keep M x y incipit
98+ for (let i = 3 ; i < segments .length - 2 ; i += 2 ) {
99+ const x1 = parseFloat (segments[i - 2 ]);
100+ const y1 = parseFloat (segments[i - 1 ]);
101+ const x2 = parseFloat (segments[i]);
102+ const y2 = parseFloat (segments[i + 1 ]);
103+ if (isNaN (x1) || isNaN (y1) || isNaN (x2) || isNaN (y2)) {
104+ continue
105+ }
106+ const controlX = (x1 + x2) / 2 ;
107+ const controlY = (y1 + y2) / 2 ;
108+ smoothedPath .push (` Q ${ x1} ${ y1} ${ controlX} ${ controlY} ` );
109+ }
110+ const lastX = segments[segments .length - 2 ];
111+ const lastY = segments[segments .length - 1 ];
112+ smoothedPath .push (` L ${ lastX} ${ lastY} ` );
113+ return smoothedPath .join (" " );
114+ }
115+
116+ function optimizeSvgPath (path ) {
117+ const commands = path .trim ().split (/ \s + / );
118+ let optimizedPath = ' ' ;
119+ let currentCommand = ' ' ;
120+ let currentX = null , currentY = null ;
121+ for (let i = 0 ; i < commands .length ; i += 1 ) {
122+ const command = commands[i];
123+ if (isNaN (command)) {
124+ currentCommand = command;
125+ if (currentCommand === ' M' || currentCommand === ' L' ) {
126+ currentX = parseFloat (commands[++ i]);
127+ currentY = parseFloat (commands[++ i]);
128+ optimizedPath += ` ${ currentCommand}${ currentX} ${ currentY} ` ;
129+ } else if (currentCommand === ' Q' ) {
130+ const cx = parseFloat (commands[++ i]);
131+ const cy = parseFloat (commands[++ i]);
132+ const x = parseFloat (commands[++ i]);
133+ const y = parseFloat (commands[++ i]);
134+
135+ if (cx === currentX && cy === currentY) {
136+ // Last point shorthand
137+ optimizedPath += ` t${ x - currentX} ${ y - currentY} ` ;
138+ } else {
139+ optimizedPath += ` q${ cx - currentX} ${ cy - currentY} ${ x - currentX} ${ y - currentY} ` ;
140+ }
141+ currentX = x;
142+ currentY = y;
143+ }
144+ } else {
145+ const x = parseFloat (command);
146+ const y = parseFloat (commands[++ i]);
147+ if (currentCommand === ' L' ) {
148+ const dx = x - currentX;
149+ const dy = y - currentY;
150+
151+ if (dx === 0 ) {
152+ // Vertical line
153+ optimizedPath += ` v${ dy} ` ;
154+ } else if (dy === 0 ) {
155+ // Horizontal line
156+ optimizedPath += ` h${ dx} ` ;
157+ } else {
158+ // Diagonal line
159+ optimizedPath += ` l${ dx} ${ dy} ` ;
160+ }
161+ currentX = x;
162+ currentY = y;
163+ } else if (currentCommand === ' Q' ) {
164+ const cx = x;
165+ const cy = y;
166+ const nx = parseFloat (commands[++ i]);
167+ const ny = parseFloat (commands[++ i]);
168+
169+ if (cx === currentX && cy === currentY) {
170+ optimizedPath += ` t${ nx - currentX} ${ ny - currentY} ` ;
171+ } else {
172+ optimizedPath += ` q${ cx - currentX} ${ cy - currentY} ${ nx - currentX} ${ ny - currentY} ` ;
173+ }
174+ currentX = nx;
175+ currentY = ny;
176+ }
177+ }
178+ }
179+ return optimizedPath;
180+ }
181+
94182function stopDrawing () {
95183 if (isDrawing .value ) {
96- stack .value .push (currentPath .value );
184+ stack .value .push (optimizeSvgPath ( smoothPath ( currentPath .value )) );
97185 redoStack .value = [];
98186 currentPath .value = " " ;
99187 }
@@ -276,6 +364,7 @@ function reset() {
276364}
277365.vue-ui-pen-and-paper-path {
278366 stroke-linecap : round ;
367+ stroke-linejoin : round ;
279368}
280369.vue-ui-pen-and-paper-path-drawing {
281370 stroke-width : 2 ;
0 commit comments