1 /* B.Shapr
2  * Beat / envelope shaper LV2 plugin
3  *
4  * Copyright (C) 2019 by Sven Jähnichen
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3, or (at your option)
9  * any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19  */
20 
21 #ifndef SHAPE_HPP_
22 #define SHAPE_HPP_
23 
24 #include <cstddef>
25 #include <cstdio>
26 #include <cstdint>
27 #include <cmath>
28 // #include <iostream>
29 #include "BUtilities/Point.hpp"
30 #include "Node.hpp"
31 #include "StaticArrayList.hpp"
32 
33 #define MAPRES 1024
34 
35 template<size_t sz>
36 class Shape
37 {
38 public:
39 	Shape ();
40 	Shape (const StaticArrayList<Node, sz> nodes, double transformFactor = 1.0, double transformOffset = 0.0);
41 	virtual ~Shape ();
42 
43 	bool operator== (const Shape<sz>& rhs);
44 	bool operator!= (const Shape<sz>& rhs);
45 
46 	void setTransformation (const double transformFactor, const double transformOffset);
47 	virtual void clearShape ();
48 	virtual void setDefaultShape ();
49 	bool isDefault () const;
50 	size_t size () const;
51 	Node getRawNode (const size_t nr) const;
52 	Node getNode (const size_t nr) const;
53 	size_t findRawNode (const Node& node);
54 
55 	bool validateNode (const size_t nr);
56 	bool validateShape ();
57 
58 	bool appendRawNode (const Node& node);
59 	bool appendNode (const Node& node);
60 	bool insertRawNode (const size_t pos, const Node& node);
61 	bool insertNode (const size_t pos, const Node& node);
62 	bool insertRawNode (const Node& node);
63 	bool insertNode (const Node& node);
64 	bool changeRawNode (const size_t pos, const Node& newnode);
65 	bool changeNode (const size_t pos, const Node& newnode);
66 	bool deleteNode (const size_t pos);
67 
68 	double getMapRawValue (const double x) const;
69 	double getMapValue (const double x) const;
70 	double* getMap ();
71 
72 protected:
73 	double transform (const double value) const;
74 	double retransform (const double value) const;
75 	Node transformNode (const Node& node) const;
76 	Node retransformNode (const Node& node) const;
77 	virtual void drawLineOnMap (const BUtilities::Point p1, const BUtilities::Point p2);
78 	BUtilities::Point getPointPerc (const BUtilities::Point p1, const BUtilities::Point p2, const double perc) const;
79 	virtual void renderBezier (const Node& n1, const Node& n2);
80 
81 	StaticArrayList<Node, sz> nodes_;
82 	double map_[MAPRES];
83 	double factor_;
84 	double offset_;
85 
86 };
87 
Shape()88 template<size_t sz> Shape<sz>::Shape () : nodes_ (), map_ {0.0}, factor_ (1.0), offset_ (0.0) {}
89 
Shape(const StaticArrayList<Node,sz> nodes,double transformFactor,double transformOffset)90 template<size_t sz> Shape<sz>::Shape (const StaticArrayList<Node, sz> nodes, double transformFactor, double transformOffset) :
91 nodes_ (nodes), map_ {0.0}, factor_ (transformFactor), offset_ (transformFactor) {}
92 
~Shape()93 template<size_t sz> Shape<sz>::~Shape () {}
94 
operator ==(const Shape<sz> & rhs)95 template<size_t sz> bool Shape<sz>::operator== (const Shape<sz>& rhs)
96 {
97 	if (size () != rhs.size ()) return false;
98 	for (unsigned int i = 0; i < size (); ++i) if (nodes_[i] != rhs.nodes_[i]) return false;
99 	return true;
100 }
101 
operator !=(const Shape<sz> & rhs)102 template<size_t sz> bool Shape<sz>::operator!= (const Shape<sz>& rhs) {return !(*this == rhs);}
103 
setTransformation(const double transformFactor,const double transformOffset)104 template<size_t sz> void Shape<sz>::setTransformation (const double transformFactor, const double transformOffset)
105 {
106 	// Prevent div by zero
107 	if (transformFactor == 0.0) return;
108 
109 	factor_ = transformFactor;
110 	offset_ = transformOffset;
111 }
112 
clearShape()113 template<size_t sz> void Shape<sz>::clearShape ()
114 {
115 	while (!nodes_.empty ()) nodes_.pop_back ();
116 	for (int i = 0; i < MAPRES; ++i) map_[i] = 0;
117 }
118 
setDefaultShape()119 template<size_t sz> void Shape<sz>::setDefaultShape ()
120 {
121 	clearShape ();
122 	nodes_.push_back ({NodeType::END_NODE, {0, 0}, {0, 0}, {0, 0}});
123 	nodes_.push_back ({NodeType::END_NODE, {1, 0}, {0, 0}, {0, 0}});
124 	renderBezier (nodes_[0], nodes_[1]);
125 }
126 
isDefault() const127 template<size_t sz>bool Shape<sz>::isDefault () const
128 {
129 	return ((nodes_.size == 2) && (nodes_[0] == Node {NodeType::END_NODE, {0, 0}, {0, 0}, {0, 0}}));
130 }
131 
size() const132 template<size_t sz>size_t Shape<sz>::size () const {return nodes_.size;}
133 
getRawNode(const size_t nr) const134 template<size_t sz>Node Shape<sz>::getRawNode (const size_t nr) const {return nodes_[nr];}
135 
getNode(const size_t nr) const136 template<size_t sz>Node Shape<sz>::getNode (const size_t nr) const {return retransformNode (getRawNode (nr));}
137 
findRawNode(const Node & node)138 template<size_t sz>size_t Shape<sz>::findRawNode (const Node& node)
139 {
140 	for (int i = 0; i < nodes_.size; ++i)
141 	{
142 		if (nodes_[i] == node) return i;
143 	}
144 	return nodes_.size;
145 }
146 
appendRawNode(const Node & node)147 template<size_t sz> bool Shape<sz>::appendRawNode (const Node& node)
148 {
149 	if (nodes_.size < sz)
150 	{
151 		nodes_.push_back (node);
152 		return true;
153 	}
154 
155 	return false;
156 }
157 
appendNode(const Node & node)158 template<size_t sz> bool Shape<sz>::appendNode (const Node& node)
159 {
160 	return appendRawNode (transformNode (node));
161 }
162 
insertRawNode(const size_t pos,const Node & node)163 template<size_t sz> bool Shape<sz>::insertRawNode (const size_t pos, const Node& node)
164 {
165 	// Nodes full => errorNode
166 	if (nodes_.size >= sz) return false;
167 
168 	size_t p = pos;
169 
170 	// Insert
171 	if (p < nodes_.size) nodes_.insert (nodes_.begin() + p, node);
172 
173 	// Otherwise append
174 	else
175 	{
176 		p = nodes_.size;
177 		nodes_.push_back (node);
178 	}
179 
180 	// Validate node and its neighbors
181 	if (!validateNode (p)) return false;
182 	if ((p > 0) && (!validateNode (p - 1))) return false;
183 	if ((p + 1 < nodes_.size) && (!validateNode (p + 1))) return false;
184 
185 	// Update map
186 	for (unsigned int i = (p >= 2 ? p - 2 : 0); (i <= p + 1) && (i + 1 < nodes_.size); ++ i) renderBezier (nodes_[i], nodes_[i + 1]);
187 	return true;
188 }
189 
insertNode(const size_t pos,const Node & node)190 template<size_t sz> bool Shape<sz>::insertNode (const size_t pos, const Node& node)
191 {
192 	return insertRawNode (pos, transformNode (node));
193 }
194 
insertRawNode(const Node & node)195 template<size_t sz> bool Shape<sz>::insertRawNode (const Node& node)
196 {
197 	// Find position
198 	size_t pos = nodes_.size;
199 	for (unsigned int i = 0; i < nodes_.size; ++i)
200 	{
201 		if (node.point.x < nodes_[i].point.x)
202 		{
203 			// Add if not redundant
204 			if ((i > 0) && (node != nodes_[i - 1]))
205 			{
206 				pos = i;
207 				break;
208 			}
209 
210 			else return false;
211 		}
212 	}
213 
214 	// Insert
215 	return insertRawNode (pos, node);
216 }
217 
insertNode(const Node & node)218 template<size_t sz> bool Shape<sz>::insertNode (const Node& node)
219 {
220 	return insertRawNode (transformNode (node));
221 }
222 
changeRawNode(const size_t pos,const Node & node)223 template<size_t sz> bool Shape<sz>::changeRawNode (const size_t pos, const Node& node)
224 {
225 	if (pos >= nodes_.size) return false;
226 	nodes_[pos] = node;
227 
228 	// Validate node and its neighbors
229 	if (!validateNode (pos)) return false;
230 	if ((pos > 0) && (!validateNode (pos - 1))) return false;
231 	if ((pos + 1 < nodes_.size) && (!validateNode (pos + 1))) return false;
232 
233 	// Update map
234 	for (unsigned int i = (pos >= 2 ? pos - 2 : 0); (i <= pos + 1) && (i + 1 < nodes_.size); ++i) renderBezier (nodes_[i], nodes_[i + 1]);
235 
236 	return true;
237 }
238 
changeNode(const size_t pos,const Node & node)239 template<size_t sz> bool Shape<sz>::changeNode (const size_t pos, const Node& node)
240 {
241 	return changeRawNode (pos, transformNode (node));
242 }
243 
deleteNode(const size_t pos)244 template<size_t sz> bool Shape<sz>::deleteNode (const size_t pos)
245 {
246 	// Only deletion of middle nodes allowed
247 	if ((pos == 0) || (pos >= nodes_.size - 1)) return false;
248 
249 	nodes_.erase (nodes_.begin() + pos);
250 
251 	// Validate neighbor nodes
252 	if (!validateNode (pos - 1)) return false;
253 	if (!validateNode (pos)) return false;
254 
255 	// Update map
256 	for (unsigned int i = (pos >= 2 ? pos - 2 : 0); (i <= pos) && (i + 1 < nodes_.size); ++ i) renderBezier (nodes_[i], nodes_[i + 1]);
257 	return true;
258 }
259 
validateNode(const size_t nr)260 template<size_t sz> bool Shape<sz>::validateNode (const size_t nr)
261 {
262 	// Exception: Invalid parameters
263 	if (nr >= nodes_.size)
264 	{
265 		fprintf (stderr, "BSchaffl.lv2: Node validation called with invalid parameters (node: %li).\n", nr);
266 		return false;
267 	}
268 
269 	// Exception: Invalid node order
270 	if ((nodes_.size >= 3) && (nr > 1) && (nr < nodes_.size - 1) && (nodes_[nr-1].point.x > nodes_[nr+1].point.x))
271 	{
272 		fprintf (stderr, "BSchaffl.lv2: Corrupt node data at node %li (%f, %f). Reset shape.\n", nr, nodes_[nr].point.x, nodes_[nr].point.y);
273 		setDefaultShape ();
274 		return false;
275 	}
276 
277 	// Start node
278 	if (nr == 0)
279 	{
280 		// Check: Only end nodes on start position
281 		if  (nodes_[0].nodeType != NodeType::END_NODE) nodes_[0] = {NodeType::END_NODE, {0, 0}, {0, 0}, {0, 0}};
282 
283 		// Check: Start position
284 		if (nodes_[0].point.x != 0) nodes_[0].point.x = 0;
285 
286 		// Check: No handles
287 		nodes_[0].handle1 = BUtilities::Point (0, 0);
288 		nodes_[0].handle2 = BUtilities::Point (0, 0);
289 	}
290 
291 	// End node
292 	else if (nr == nodes_.size - 1)
293 	{
294 		// Check: Only end nodes on end position
295 		if (nodes_[nr].nodeType != NodeType::END_NODE)
296 		{
297 			nodes_[nr] = {NodeType::END_NODE, {1, nodes_[0].point.y}, {0, 0}, {0, 0}};
298 		}
299 
300 		// Check: No handles
301 		nodes_[nr].handle1 = BUtilities::Point (0, 0);
302 		nodes_[nr].handle2 = BUtilities::Point (0, 0);
303 	}
304 
305 	// Middle nodes
306 	else
307 	{
308 		// Check: No end nodes in the middle
309 		if (nodes_[nr].nodeType == NodeType::END_NODE) nodes_[nr].nodeType = NodeType::CORNER_NODE;
310 
311 		// Check: Nodes point order
312 		if (nodes_[nr].point.x < nodes_[nr - 1].point.x) nodes_[nr].point.x = nodes_[nr - 1].point.x;
313 		if (nodes_[nr].point.x > nodes_[nr + 1].point.x) nodes_[nr].point.x = nodes_[nr + 1].point.x;
314 
315 		// Check: POINT_NODE without handles
316 		if (nodes_[nr].nodeType == NodeType::POINT_NODE)
317 		{
318 			nodes_[nr].handle1 = BUtilities::Point (0, 0);
319 			nodes_[nr].handle2 = BUtilities::Point (0, 0);
320 		}
321 
322 		// Check: Handles order
323 		if (nodes_[nr].handle1.x > 0) nodes_[nr].handle1.x = 0;
324 		if (nodes_[nr].handle2.x < 0) nodes_[nr].handle2.x = 0;
325 
326 		// Check: AUTO_SMOOTH_NODE with symmetric handles with the half size of the distance to the closest neighbor point
327 		if (nodes_[nr].nodeType == NodeType::AUTO_SMOOTH_NODE)
328 		{
329 			double dist = (nodes_[nr].point.x - nodes_[nr-1].point.x > nodes_[nr+1].point.x - nodes_[nr].point.x ?
330 						   nodes_[nr+1].point.x - nodes_[nr].point.x :
331 						   nodes_[nr].point.x - nodes_[nr-1].point.x);
332 			double ydist = (nodes_[nr + 1].point.y - nodes_[nr - 1].point.y);
333 			double yamp = (fabs (nodes_[nr - 1].point.y != 0) && fabs (nodes_[nr + 1].point.y != 0) ?
334 								(fabs (nodes_[nr - 1].point.y) < fabs (nodes_[nr + 1].point.y) ?
335 										fabs (nodes_[nr - 1].point.y) / (fabs (nodes_[nr - 1].point.y) + fabs (nodes_[nr + 1].point.y)) :
336 										fabs (nodes_[nr + 1].point.y) / (fabs (nodes_[nr - 1].point.y) + fabs (nodes_[nr + 1].point.y))) :
337 								0);
338 			nodes_[nr].handle1.x = -dist / 2;
339 			nodes_[nr].handle1.y = -ydist * yamp;
340 			nodes_[nr].handle2.x = dist / 2;
341 			nodes_[nr].handle2.y = ydist * yamp;
342 		}
343 
344 		// Check: SYMMETRIC_SMOOTH_NODE must be symmetric
345 		else if (nodes_[nr].nodeType == NodeType::SYMMETRIC_SMOOTH_NODE)
346 		{
347 			//Check if handle1 overlaps neighbor point
348 			if (nodes_[nr].point.x + nodes_[nr].handle1.x < nodes_[nr-1].point.x)
349 			{
350 				double f = (nodes_[nr-1].point.x - nodes_[nr].point.x) / nodes_[nr].handle1.x;
351 				nodes_[nr].handle1.x *= f;
352 				nodes_[nr].handle1.y *= f;
353 			}
354 
355 			// Make handele2 symmetric to handle1
356 			nodes_[nr].handle2 = BUtilities::Point (0, 0) - nodes_[nr].handle1;
357 
358 			//Check if handle2 overlaps neighbor point
359 			if (nodes_[nr].point.x + nodes_[nr].handle2.x > nodes_[nr+1].point.x)
360 			{
361 				double f = (nodes_[nr+1].point.x - nodes_[nr].point.x) / nodes_[nr].handle2.x;
362 				nodes_[nr].handle2.x *= f;
363 				nodes_[nr].handle2.y *= f;
364 				nodes_[nr].handle1 = BUtilities::Point (0, 0) - nodes_[nr].handle2;
365 			}
366 		}
367 
368 		// Check: SMOOTH_NODE handles point to opposite directions
369 		else if (nodes_[nr].nodeType == NodeType::SMOOTH_NODE)
370 		{
371 			//Check if handle1 overlaps neighbor point
372 			if (nodes_[nr].point.x + nodes_[nr].handle1.x < nodes_[nr-1].point.x)
373 			{
374 				double f = (nodes_[nr-1].point.x - nodes_[nr].point.x) / nodes_[nr].handle1.x;
375 				nodes_[nr].handle1.x *= f;
376 				nodes_[nr].handle1.y *= f;
377 			}
378 
379 			// Calculate handle distances
380 			double dist1 = sqrt (nodes_[nr].handle1.x * nodes_[nr].handle1.x + nodes_[nr].handle1.y * nodes_[nr].handle1.y);
381 			double dist2 = sqrt (nodes_[nr].handle2.x * nodes_[nr].handle2.x + nodes_[nr].handle2.y * nodes_[nr].handle2.y);
382 
383 			// Recalculate handle2
384 			if ((dist1 != 0) && (dist2 != 0))
385 			{
386 				nodes_[nr].handle2.x = -(nodes_[nr].handle1.x * dist2 / dist1);
387 				nodes_[nr].handle2.y = -(nodes_[nr].handle1.y * dist2 / dist1);
388 			}
389 
390 			//Check if handle2 overlaps neighbor point
391 			if (nodes_[nr].point.x + nodes_[nr].handle2.x > nodes_[nr+1].point.x)
392 			{
393 				double f = (nodes_[nr+1].point.x - nodes_[nr].point.x) / nodes_[nr].handle2.x;
394 				nodes_[nr].handle2.x *= f;
395 				nodes_[nr].handle2.y *= f;
396 			}
397 		}
398 
399 		// Check: CORNER_NODE
400 		else if (nodes_[nr].nodeType == NodeType::CORNER_NODE)
401 		{
402 			//Check if handle1 overlaps neighbor point
403 			if (nodes_[nr].point.x + nodes_[nr].handle1.x < nodes_[nr-1].point.x)
404 			{
405 				double f = (nodes_[nr-1].point.x - nodes_[nr].point.x) / nodes_[nr].handle1.x;
406 				nodes_[nr].handle1.x *= f;
407 				nodes_[nr].handle1.y *= f;
408 			}
409 
410 			//Check if handle2 overlaps neighbor point
411 			if (nodes_[nr].point.x + nodes_[nr].handle2.x > nodes_[nr+1].point.x)
412 			{
413 				double f = (nodes_[nr+1].point.x - nodes_[nr].point.x) / nodes_[nr].handle2.x;
414 				nodes_[nr].handle2.x *= f;
415 				nodes_[nr].handle2.y *= f;
416 			}
417 		}
418 	}
419 
420 	return true;
421 }
422 
validateShape()423 template<size_t sz> bool Shape<sz>::validateShape ()
424 {
425 	// TODO Sort ???
426 
427 	// Validate nodes
428 	bool status = true;
429 	for (unsigned int i = 0; i < nodes_.size; ++i)
430 	{
431 		if (!validateNode (i)) status = false;
432 	}
433 
434 	// Update map
435 	for (unsigned int i = 0; i + 1 < nodes_.size; ++i) renderBezier (nodes_[i], nodes_[i+1]);
436 
437 	return status;
438 }
439 
transform(const double value) const440 template<size_t sz> double Shape<sz>::transform (const double value) const
441 {
442 	return (value - offset_) / factor_;
443 }
444 
retransform(const double value) const445 template<size_t sz> double Shape<sz>::retransform (const double value) const
446 {
447 	return factor_ * value + offset_;
448 }
449 
transformNode(const Node & node) const450 template<size_t sz> Node Shape<sz>::transformNode (const Node& node) const
451 {
452 	return Node
453 	(
454 		node.nodeType,
455 		BUtilities::Point (node.point.x, transform (node.point.y)),
456 		BUtilities::Point (node.handle1.x, node.handle1.y / factor_),
457 		BUtilities::Point (node.handle2.x, node.handle2.y / factor_)
458 	);
459 }
460 
retransformNode(const Node & node) const461 template<size_t sz> Node Shape<sz>::retransformNode (const Node& node) const
462 {
463 	return Node
464 	(
465 		node.nodeType,
466 		BUtilities::Point (node.point.x, retransform (node.point.y)),
467 		BUtilities::Point (node.handle1.x, node.handle1.y * factor_),
468 		BUtilities::Point (node.handle2.x, node.handle2.y * factor_)
469 	);
470 }
471 
drawLineOnMap(BUtilities::Point p1,BUtilities::Point p2)472 template<size_t sz> void Shape<sz>::drawLineOnMap (BUtilities::Point p1, BUtilities::Point p2)
473 {
474 	if (p1.x < p2.x)
475 	{
476 		for (double x = p1.x; (x <= p2.x) && (x <= 1.0); x += (1.0 / MAPRES))
477 		{
478 			uint32_t i = ((uint32_t) (x * MAPRES)) % MAPRES;
479 			map_[i] = p1.y + (p2.y - p1.y) * (x - p1.x) / (p2.x - p1.x);
480 		}
481 	}
482 
483 	else
484 	{
485 		uint32_t i = ((uint32_t) (p2.x * MAPRES)) % MAPRES;
486 		map_ [i] = p2.y;
487 	}
488 }
489 
getPointPerc(const BUtilities::Point p1,const BUtilities::Point p2,const double perc) const490 template<size_t sz> BUtilities::Point Shape<sz>::getPointPerc (const BUtilities::Point p1, const BUtilities::Point p2 , const double perc) const
491 {
492 	BUtilities::Point p;
493 	p.x = p1.x + (p2.x - p1.x) * perc;
494 	p.y = p1.y + (p2.y - p1.y) * perc;
495 	return p;
496 }
497 
renderBezier(const Node & n1,const Node & n2)498 template<size_t sz> void Shape<sz>::renderBezier (const Node& n1, const Node& n2)
499 {
500 	// Interpolate Bezier curve
501 	BUtilities::Point p1 = n1.point;
502 	BUtilities::Point p2 = n1.point + n1.handle2;
503 	BUtilities::Point p4 = n2.point;
504 	BUtilities::Point p3 = n2.point + n2.handle1;
505 	BUtilities::Point py = p1;
506 	double step = 1 / (fabs (n2.point.x - n1.point.x) * MAPRES + 1);
507 
508 	for (double t = 0; t < 1; t += step)
509 	{
510 	    BUtilities::Point pa = getPointPerc (p1, p2, t);
511 	    BUtilities::Point pb = getPointPerc (p2, p3, t);
512 	    BUtilities::Point pc = getPointPerc (p3, p4, t);
513 	    BUtilities::Point pm = getPointPerc (pa, pb, t);
514 	    BUtilities::Point pn = getPointPerc (pb, pc, t);
515 	    BUtilities::Point pz = getPointPerc (pm, pn, t);
516 
517 	    drawLineOnMap (py, pz);
518 	    py = pz;
519 	}
520 	drawLineOnMap (py,p4);
521 }
522 
getMapRawValue(const double x) const523 template<size_t shapesize> double Shape<shapesize>::getMapRawValue (const double x) const
524 {
525 	double mapx = fmod (x * MAPRES, MAPRES);
526 	double xmod = mapx - int (mapx);
527 
528 	return (1 - xmod) * map_[int (mapx)] + xmod * map_[int (mapx + 1) % MAPRES];
529 }
530 
getMapValue(const double x) const531 template<size_t shapesize> double Shape<shapesize>::getMapValue (const double x) const
532 {
533 	return retransform (getMapRawValue (x));
534 }
535 
getMap()536 template<size_t sz> double* Shape<sz>::getMap () {return &map_[0];}
537 
538 /*
539 template<size_t sz> std::ostream &operator<<(std::ostream &output, Shape<sz>& shape)
540 {
541 	output << shape.nodes;
542 	return output;
543 }
544 */
545 #endif /* SHAPE_HPP_ */
546