Curve.hx 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784
  1. package hrt.prefab;
  2. #if editor
  3. import hide.prefab.EditContext;
  4. import hide.prefab.HideProps;
  5. #end
  6. import hrt.prefab.fx.Value;
  7. using Lambda;
  8. @:build(hrt.prefab.Macros.buildSerializable())
  9. class CurveHandle {
  10. @:s public var dv: Float = -1; // Force serialization of 0 values for retrocompat
  11. @:s public var dt: Float = -1; // Force serialization of 0 values for retrocompat
  12. public function new(t, v) {
  13. this.dt = t;
  14. this.dv = v;
  15. }
  16. }
  17. enum abstract CurveKeyMode(Int) {
  18. var Aligned = 0;
  19. var Free = 1;
  20. var Linear = 2;
  21. var Constant = 3;
  22. }
  23. enum abstract CurveBlendMode(Int) {
  24. var None = 0;
  25. var Blend = 1;
  26. var RandomBlend = 2;
  27. var Reference = 3;
  28. }
  29. @:build(hrt.prefab.Macros.buildSerializable())
  30. class CurveKey {
  31. @:s public var time: Float = -1; // Force serialization of 0 values for retrocompat
  32. @:s public var value: Float = -1; // Force serialization of 0 values for retrocompat
  33. @:s public var mode: CurveKeyMode = Aligned;
  34. @:s public var prevHandle: CurveHandle;
  35. @:s public var nextHandle: CurveHandle;
  36. public function new() {}
  37. }
  38. typedef CurveKeys = Array<CurveKey>;
  39. class Curve extends Prefab {
  40. @:s public var keyMode : CurveKeyMode = Linear;
  41. @:s public var keys : CurveKeys = [];
  42. @:s public var previewKeys : CurveKeys = [];
  43. @:s public var blendMode : CurveBlendMode = None;
  44. @:s public var loop : Bool = false;
  45. @:s public var blendParam : String = null;
  46. @:s public var offset : Float = 0.0;
  47. @:s public var scale : Float = 1.0;
  48. public var maxTime : Float = 5000.;
  49. public var duration(get, never): Float;
  50. public var minValue : Float = 0.;
  51. public var maxValue : Float = 0.;
  52. public var color : Int;
  53. public var hidden : Bool = false;
  54. public var lock : Bool = false;
  55. public var selected : Bool = false;
  56. var refCurve : Curve = null;
  57. function get_duration() {
  58. if (blendMode == Reference) {
  59. return getRef()?.duration ?? 0.0;
  60. }
  61. if(keys.length == 0) return 0.0;
  62. if (blendMode == CurveBlendMode.Blend || blendMode == CurveBlendMode.RandomBlend) {
  63. var c1:Curve = cast this.children[0];
  64. var c2:Curve = cast this.children[1];
  65. return Math.min(c1.duration, c2.duration);
  66. }
  67. return keys[keys.length-1].time;
  68. }
  69. public function new(parent, shared: ContextShared) {
  70. super(parent, shared);
  71. }
  72. public override function load(o:Dynamic) {
  73. if (o.blendMode != null && !Std.isOfType(o.blendMode, Int)) {
  74. o.blendMode = Std.parseInt(o.blendMode);
  75. }
  76. super.load(o);
  77. if( keys.length == 0 ) {
  78. addKey(0.0, 0.0);
  79. addKey(1.0, 1.0);
  80. }
  81. name = StringTools.replace(name, ".", ":");
  82. #if editor
  83. if (Std.downcast(parent, Curve) != null) {
  84. if (StringTools.startsWith(name, parent.name)) {
  85. name = StringTools.replace(name, parent.name + ":", "");
  86. }
  87. }
  88. #end
  89. }
  90. public override function copy(o:Prefab) {
  91. super.copy(o);
  92. }
  93. override function makeInstance() {
  94. refCurve = null;
  95. }
  96. static inline function bezier(c0: Float, c1:Float, c2:Float, c3: Float, t:Float) {
  97. var u = 1 - t;
  98. return u * u * u * c0 + c1 * 3 * t * u * u + c2 * 3 * t * t * u + t * t * t * c3;
  99. }
  100. public function findKey(time: Float, tolerance: Float) {
  101. var minDist = tolerance;
  102. var closest = null;
  103. for(k in keys) {
  104. var d = hxd.Math.abs(k.time - time);
  105. if(d < minDist) {
  106. minDist = d;
  107. closest = k;
  108. }
  109. }
  110. return closest;
  111. }
  112. public function addKey(time: Float, ?val: Float, ?mode=null) {
  113. var index = 0;
  114. for(ik in 0...keys.length) {
  115. var key = keys[ik];
  116. if(time > key.time)
  117. index = ik + 1;
  118. }
  119. if(val == null)
  120. val = getVal(time);
  121. var key = new hrt.prefab.Curve.CurveKey();
  122. key.time = time;
  123. key.value = val;
  124. key.mode = mode != null ? mode : (keys[index] != null ? keys[index].mode : keyMode);
  125. keys.insert(index, key);
  126. return key;
  127. }
  128. public function addPreviewKey(time: Float, val: Float) {
  129. var key = new hrt.prefab.Curve.CurveKey();
  130. key.time = time;
  131. key.value = val;
  132. previewKeys.push(key);
  133. return key;
  134. }
  135. public function getBounds(bounds: h2d.col.Bounds = null) {
  136. if (blendMode == Reference) {
  137. return getRef()?.getBounds(bounds) ?? bounds ?? new h2d.col.Bounds();
  138. }
  139. var ret = bounds == null ? new h2d.col.Bounds() : bounds;
  140. // We always want to show 0,0
  141. ret.addPos(0.0,0.0);
  142. for(k in keys) {
  143. ret.addPos(k.time, k.value);
  144. if (k.nextHandle != null) {
  145. ret.addPos(k.time + k.nextHandle.dt, k.value + k.nextHandle.dv);
  146. }
  147. if (k.prevHandle != null) {
  148. ret.addPos(k.time + k.prevHandle.dt, k.value + k.prevHandle.dv);
  149. }
  150. }
  151. return ret;
  152. }
  153. function getRef() : Null<Curve> {
  154. if (refCurve != null)
  155. return refCurve;
  156. var root = getRoot(false);
  157. refCurve = Std.downcast(root.locatePrefab(blendParam), Curve);
  158. if (refCurve == this) refCurve = null;
  159. return refCurve;
  160. }
  161. public function makeVal() : Value {
  162. switch(blendMode) {
  163. case None:
  164. return VCurve(this);
  165. case Blend:
  166. return VBlend(Std.downcast(this.children[0], Curve)?.makeVal() ?? VConst(0.0), Std.downcast(this.children[1], Curve)?.makeVal() ?? VConst(0.0), blendParam);
  167. case RandomBlend:
  168. return VCurve(this);
  169. case Reference:
  170. var val = getRef()?.makeVal();
  171. if (val == null)
  172. return VConst(0.0);
  173. if (scale != 1.0)
  174. val = VMult(val, VConst(scale));
  175. if (offset != 0.0)
  176. val = VAdd(val, VConst(offset));
  177. return val;
  178. default:
  179. throw "invalid blendMode value";
  180. }
  181. }
  182. inline function findT(cur: CurveKey, next: CurveKey, time: Float) : {minT: Float, maxT: Float} {
  183. inline function sampleTime(t) {
  184. return bezier(
  185. cur.time,
  186. cur.time + (cur.nextHandle != null ? cur.nextHandle.dt : 0.),
  187. next.time + (next.prevHandle != null ? next.prevHandle.dt : 0.),
  188. next.time, t);
  189. }
  190. var minT = 0.;
  191. var maxT = 1.;
  192. var maxDelta = 1./ 25.;
  193. while( maxT - minT > maxDelta ) {
  194. var t = (maxT + minT) * 0.5;
  195. var x = sampleTime(t);
  196. if( x > time )
  197. maxT = t;
  198. else
  199. minT = t;
  200. }
  201. return {minT: minT, maxT: maxT};
  202. }
  203. public function getVal(time: Float) : Float {
  204. if (blendMode == Reference) {
  205. throw "getVal shoudln't be called on curves with Reference mode";
  206. }
  207. switch(keys.length) {
  208. case 0: return 0;
  209. case 1: return keys[0].value;
  210. default:
  211. }
  212. if (loop)
  213. time = time % keys[keys.length-1].time;
  214. var idx = -1;
  215. for(ik in 0...keys.length) {
  216. var key = keys[ik];
  217. if(time > key.time)
  218. idx = ik;
  219. }
  220. if(idx < 0)
  221. return keys[0].value;
  222. var cur = keys[idx];
  223. var next = keys[idx + 1];
  224. if(next == null || cur.mode == Constant)
  225. return cur.value;
  226. var T = findT(cur, next, time);
  227. var minT = T.minT;
  228. var maxT = T.maxT;
  229. inline function sampleTime(t) {
  230. return bezier(
  231. cur.time,
  232. cur.time + (cur.nextHandle != null ? cur.nextHandle.dt : 0.),
  233. next.time + (next.prevHandle != null ? next.prevHandle.dt : 0.),
  234. next.time, t);
  235. }
  236. var x0 = sampleTime(minT);
  237. var x1 = sampleTime(maxT);
  238. var dx = x1 - x0;
  239. var xfactor = dx == 0 ? 0.5 : (time - x0) / dx;
  240. inline function sampleVal(t) {
  241. return bezier(
  242. cur.value,
  243. cur.value + (cur.nextHandle != null ? cur.nextHandle.dv : 0.),
  244. next.value + (next.prevHandle != null ? next.prevHandle.dv : 0.),
  245. next.value, t);
  246. }
  247. var y0 = sampleVal(minT);
  248. var y1 = sampleVal(maxT);
  249. var y = y0 + (y1 - y0) * xfactor;
  250. return y;
  251. }
  252. public function getSum(time: Float) : Float {
  253. if (blendMode == Reference) {
  254. return getRef()?.getSum(time) ?? 0.0;
  255. }
  256. var duration = keys[keys.length-1].time;
  257. if(loop && time > duration) {
  258. var cycles = Math.floor(time / duration);
  259. return getSum(duration) * cycles + getSum(time - cycles);
  260. }
  261. var sum = 0.0;
  262. for(ik in 0...keys.length) {
  263. var key = keys[ik];
  264. if(time < key.time)
  265. break;
  266. if(ik == 0 && key.time > 0) {
  267. // Account for start of curve
  268. sum += key.time * key.value;
  269. }
  270. var nkey = keys[ik + 1];
  271. if(nkey != null) {
  272. if(time > nkey.time) {
  273. // Full interval
  274. sum += key.value * (nkey.time - key.time);
  275. if(key.mode != Constant)
  276. sum += 0.5 * (nkey.time - key.time) * (nkey.value - key.value);
  277. }
  278. else {
  279. // Split interval
  280. sum += key.value * (time - key.time);
  281. if(key.mode != Constant)
  282. sum += 0.5 * (time - key.time) * hxd.Math.lerp(key.value, nkey.value, (time - key.time) / (nkey.time - key.time));
  283. }
  284. }
  285. else {
  286. sum += key.value * (time - key.time);
  287. }
  288. }
  289. return sum;
  290. }
  291. public function sample(numPts: Int) {
  292. if (blendMode == Reference) {
  293. return getRef()?.sample(numPts) ?? [];
  294. }
  295. var vals = [];
  296. var duration = this.duration;
  297. for(i in 0...numPts) {
  298. var v = 0.0;
  299. v = getVal(duration * i/(numPts-1));
  300. vals.push(v);
  301. }
  302. return vals;
  303. }
  304. #if editor
  305. override function edit( ctx : EditContext ) {
  306. super.edit(ctx);
  307. var props = new hide.Element('
  308. <div class="group" name="Parameters">
  309. <dl>
  310. <dt>Loop curve</dt><dd><input type="checkbox" field="loop"/></dd>
  311. <dt>Blend Mode</dt><dd>
  312. <select class="blendmode-selector" id="blendMode">
  313. <option value="0">None</option>
  314. <option value="1">Blend curve</option>
  315. <option value="2">Random between two curves</option>
  316. <option value="3">Curve Reference</option>
  317. </select>
  318. </dd>
  319. <div id="parameter">
  320. <dt>Parameter</dt><dd>
  321. <select field="blendParam"></select>
  322. </dd>
  323. </div>
  324. <div id="reference">
  325. <dt>Curve Path</dt><dd>
  326. <select field="blendParam"></select>
  327. </dd>
  328. <dt>Offset</dt><dd><input type="range" min="0.0" max="1.0" field="offset"/></dd>
  329. <dt>Scale</dt><dd><input type="range" min="0.0" max="1.0" field="scale"/></dd>
  330. </div>
  331. </dl>
  332. </div>');
  333. var blendModeSelect = props.find('#blendMode');
  334. var refreshBlend = function() {
  335. var parameter = props.find('#parameter');
  336. if (blendMode != Blend) {
  337. parameter.hide();
  338. }
  339. else {
  340. var selecta = parameter.find('[field="blendParam"]');
  341. parameter.show();
  342. selecta.empty();
  343. var root = Std.downcast(getRoot(false), hrt.prefab.fx.FX);
  344. for (p in root.parameters) {
  345. selecta.append(new hide.Element('<option value="${p.name}">${p.name}</option>'));
  346. }
  347. selecta.val(blendParam);
  348. }
  349. var reference = props.find('#reference');
  350. if (blendMode != Reference) {
  351. reference.hide();
  352. }
  353. else {
  354. var selecta = reference.find('[field="blendParam"]');
  355. reference.show();
  356. selecta.empty();
  357. var root = getRoot(false);
  358. var flat = root.flatten(Curve);
  359. for (p in flat) {
  360. if (p == this) continue;
  361. var path = p.getAbsPath();
  362. selecta.append(new hide.Element('<option value="${path}">${path}</option>'));
  363. }
  364. selecta.val(blendParam);
  365. }
  366. }
  367. refreshBlend();
  368. blendModeSelect.val(Std.string(blendMode));
  369. blendModeSelect.change((_) -> {
  370. var val = blendModeSelect.val();
  371. blendMode = cast Std.parseInt(val);
  372. blendParam = null;
  373. refreshBlend();
  374. ctx.onChange(this, "blendMode");
  375. ctx.rebuildPrefab(this);
  376. });
  377. ctx.properties.add(props, this, function(pname) {
  378. if (pname == "blendParam") {
  379. refCurve = null;
  380. if (containsRefCycle()) {
  381. hide.Ide.inst.quickMessage("Curve reference create a cycle");
  382. blendParam = null;
  383. refCurve = null;
  384. }
  385. }
  386. refreshBlend();
  387. ctx.onChange(this, pname);
  388. });
  389. var ce = new hide.comp.CurveEditor(ctx.properties.undo, ctx.properties.element, false);
  390. ce.saveDisplayKey = this.getAbsPath(true);
  391. ce.curves.push(this);
  392. ce.refresh();
  393. }
  394. override function getHideProps() : HideProps {
  395. return { icon : "paint-brush", name : "Curve" };
  396. }
  397. // Returns the visual representation of this curve as an svg path `d` data
  398. public function getSvgString() : String {
  399. var data = "";
  400. var bounds = this.getBounds();
  401. if (this.keys.length > 0) {
  402. data += 'M ${bounds.xMin} ${this.keys[0].value} H ${this.keys[0].time}';
  403. for (i in 1...this.keys.length) {
  404. data += ' ';
  405. var prev = this.keys[i-1];
  406. var next = this.keys[i];
  407. switch (prev.mode) {
  408. case Linear:
  409. data += 'L ${next.time} ${next.value}';
  410. case Constant:
  411. data += 'H ${next.time} V ${next.value}';
  412. case Aligned, Free:
  413. {
  414. var prevDt = prev.nextHandle?.dt ?? 0.0;
  415. var prevDv = prev.nextHandle?.dv ?? 0.0;
  416. var nextDt = next.prevHandle?.dt ?? 0.0;
  417. var nextDv = next.prevHandle?.dv ?? 0.0;
  418. var c0 = prev.time;
  419. var c1 = prev.time + prevDt;
  420. var c2 = next.time + nextDt;
  421. var c3 = next.time;
  422. // solve bezier(c0,c1,c2,c2, x) = bezier(c0,c1,c2,c2, 0.5) for x
  423. final sqrt3 = hxd.Math.sqrt(3);
  424. var r0: Float;
  425. var r1: Float;
  426. var midt = bezier(c0,c1,c2,c3, 0.5);
  427. if (midt < c0) {
  428. // solve bezier(c0,c1,c2,c2, x) = c0 for x;
  429. var div = 2*c3-6*c2+6*c1-2*c0;
  430. var fact = sqrt3*hxd.Math.sqrt((4*c0-4*c1)*c3+3*c2*c2-6*c0*c2+4*c0*c1-c0*c0);
  431. r0 = -(fact+3*c2-6*c1+3*c0)/div;
  432. r1 = (fact-3*c2+6*c1-3*c0)/div;
  433. }
  434. else if (midt > c3) {
  435. // solve bezier(c0,c1,c2,c2, x) = c3 for x;
  436. var div = 2*c3-6*c2+6*c1-2*c0;
  437. var fact = sqrt3*hxd.Math.sqrt(-c3*c3+(4*c2-6*c1+4*c0)*c3-4*c0*c2+3*c1*c1);
  438. r0 = -((fact+c3-3*c1+2*c0)/(div));
  439. r1 = (fact-c3+3*c1-2*c0)/(div);
  440. }
  441. else {
  442. // solve bezier(c0,c1,c2,c2, x) = 0.5 for x;
  443. r0 = -((sqrt3*hxd.Math.sqrt(-c3*c3+(2*c2-14*c1+14*c0)*c3+15*c2*c2+(-(18*c1)-14*c0)*c2+15*c1*c1+2*c0*c1-c0*c0)+c3+3*c2-9*c1+5*c0)/(4*c3-12*c2+12*c1-4*c0));
  444. r1 = (sqrt3*hxd.Math.sqrt(-c3*c3+(2*c2-14*c1+14*c0)*c3+15*c2*c2+(-(18*c1)-14*c0)*c2+15*c1*c1+2*c0*c1-c0*c0)-c3-3*c2+9*c1-5*c0)/(4*c3-12*c2+12*c1-4*c0);
  445. }
  446. if (r0 > r1) {
  447. var tmp = r1;
  448. r1 = r0;
  449. r0 = tmp;
  450. }
  451. if (r1 > 1.0) {
  452. r1 = r0;
  453. r0 = 0.0;
  454. }
  455. if (r0 < 0.0) {
  456. r0 = r1;
  457. r1 = 1.0;
  458. }
  459. inline function check(a) {
  460. return (Math.isNaN(a) || a < 0 || a > 1);
  461. }
  462. if (check(r0) && check(r1)) {
  463. data += 'C ${prev.time + prevDt} ${prev.value + prevDv}, ${next.time + nextDt} ${next.value + nextDv}, ${next.time} ${next.value}';
  464. }
  465. else {
  466. if (std.Math.isNaN(r0)) {
  467. r0 = 0.0;
  468. }
  469. if (std.Math.isNaN(r1)) {
  470. r1 = 1.0;
  471. }
  472. r0 = hxd.Math.clamp(r0, 0.0, 1.0);
  473. r1 = hxd.Math.clamp(r1, 0.0, 1.0);
  474. inline function b(t) {
  475. bezier(c0,c1,c2,c3, t);
  476. }
  477. // split the curve in two if its loops on itself
  478. inline function sample(c0 : Float, c1: Float, c2: Float, c3:Float, t: Float) : {startHandle: Float, value: Float, inValue: Float, outValue: Float, endHandle: Float} {
  479. var a = c0 + (c1-c0) * t;
  480. var b = c1 + (c2-c1) * t;
  481. var c = c2 + (c3-c2) * t;
  482. var d = a + (b-a) * t;
  483. var e = b + (c-b) * t;
  484. var x = d + (e-d) * t;
  485. return {startHandle: a, value:x, inValue: d, outValue: e, endHandle: c};
  486. }
  487. var p0x = sample(c0, c1, c2, c3, r0);
  488. var p1x = sample(c0, c1, c2, c3, r1);
  489. var p0y = sample(prev.value, prev.value+prevDv, next.value + nextDv, next.value, r0);
  490. var p1y = sample(prev.value, prev.value+prevDv, next.value + nextDv, next.value, r1);
  491. if (r0 > 0.0) {
  492. data += 'C ${p0x.startHandle} ${p0y.startHandle}, ${p0x.inValue} ${p0y.inValue}, ${p0x.value} ${p0y.value} ';
  493. }
  494. data += 'L ${hxd.Math.clamp(p1x.value, prev.time, next.time)} ${p1y.value}';
  495. if (r1 < 1.0) {
  496. data += ' C ${p1x.outValue} ${p1y.outValue}, ${p1x.endHandle} ${p1y.endHandle}, ${next.time} ${next.value}';
  497. }
  498. }
  499. }
  500. }
  501. }
  502. }
  503. return data;
  504. }
  505. #end
  506. function containsRefCycle(?visited: Map<Curve, Bool>) : Bool {
  507. if (blendMode != Reference) return false;
  508. var visited = visited?.copy() ?? [];
  509. var cur = this;
  510. while (cur != null) {
  511. if (visited.get(cur) != null)
  512. {
  513. return true;
  514. }
  515. visited.set(cur, true);
  516. if (cur.blendMode == Blend || cur.blendMode == RandomBlend) {
  517. for (i in 0...2) {
  518. var subCurve = Std.downcast(cur.children[i], Curve);
  519. if (subCurve.containsRefCycle(visited)) {
  520. return true;
  521. }
  522. }
  523. }
  524. cur = cur.getRef();
  525. }
  526. return false;
  527. }
  528. public static function getCurve(parent : Prefab, name: String, onlyEnabled=true) {
  529. for(c in parent.children) {
  530. if(onlyEnabled && !c.enabled) continue;
  531. if(c.name != name) continue;
  532. var curve = c.to(Curve);
  533. if(curve == null) continue;
  534. return curve;
  535. }
  536. return null;
  537. }
  538. public static function getCurves(parent: Prefab, prefix: String) {
  539. var ret = null;
  540. for(c in parent.children) {
  541. if(!c.enabled) continue;
  542. var idx = c.name.indexOf(":");
  543. var curvePrefix = (idx >= 0) ? c.name.substr(0, idx) : c.name;
  544. if(curvePrefix != prefix)
  545. continue;
  546. var curve = c.to(Curve);
  547. if(curve == null) continue;
  548. if (ret == null) ret = [];
  549. ret.push(curve);
  550. }
  551. return ret;
  552. }
  553. public static function getGroups(curves: Array<Curve>) {
  554. var groups : Array<{name: String, items: Array<Curve>}> = [];
  555. for(c in curves) {
  556. var prefix = c.name.split(":")[0];
  557. var g = groups.find(g -> g.name == prefix);
  558. if(g == null) {
  559. groups.push({
  560. name: prefix,
  561. items: [c]
  562. });
  563. }
  564. else {
  565. g.items.push(c);
  566. }
  567. }
  568. return groups;
  569. }
  570. #if castle
  571. public function initFromCDB(curve: cdb.Types.Curve) {
  572. keys = [];
  573. var nbPoints = Std.int(curve.data.length / 6);
  574. for (pointIdx in 0...nbPoints) {
  575. var x = curve.data[pointIdx * 6 + 0];
  576. var y = curve.data[pointIdx * 6 + 1];
  577. var prevHandleX = curve.data[pointIdx * 6 + 2];
  578. var prevHandleY = curve.data[pointIdx * 6 + 3];
  579. var nextHandleX = curve.data[pointIdx * 6 + 4];
  580. var nextHandleY = curve.data[pointIdx * 6 + 5];
  581. var mode : hrt.prefab.Curve.CurveKeyMode = Free;
  582. if (Math.isNaN(prevHandleX) || prevHandleX == cdb.Types.Curve.HandleData) {
  583. mode = cast Std.int(prevHandleY);
  584. }
  585. else {
  586. var pt1 = inline new h2d.col.Point(prevHandleX, prevHandleY);
  587. var pt2 = inline new h2d.col.Point(nextHandleX, nextHandleY);
  588. pt1.normalize();
  589. pt2.normalize();
  590. var dot = pt1.dot(pt2);
  591. if (Math.abs(dot + 1.0) < 0.01) {
  592. mode = Aligned;
  593. }
  594. }
  595. var key = addKey(x, y, mode);
  596. if (mode == Free || mode == Aligned) {
  597. key.prevHandle = new hrt.prefab.Curve.CurveHandle(Math.isFinite(prevHandleX) ? prevHandleX : 0.0, Math.isFinite(prevHandleY) ? prevHandleY : 0.0);
  598. key.nextHandle = new hrt.prefab.Curve.CurveHandle(Math.isFinite(nextHandleX) ? nextHandleX : 0.0, Math.isFinite(nextHandleY) ? nextHandleY : 0.0);
  599. }
  600. }
  601. }
  602. public function toCDB() : cdb.Types.Curve {
  603. var data : Array<Float>= [];
  604. for (key in keys) {
  605. data.push(key.time);
  606. data.push(key.value);
  607. switch(key.mode) {
  608. case Free, Aligned:
  609. data.push(key.prevHandle?.dt ?? 0.0);
  610. data.push(key.prevHandle?.dv ?? 0.0);
  611. data.push(key.nextHandle?.dt ?? 0.0);
  612. data.push(key.nextHandle?.dv ?? 0.0);
  613. case Constant, Linear:
  614. data.push(cdb.Types.Curve.HandleData);
  615. data.push((cast key.mode:Int));
  616. data.push(cdb.Types.Curve.HandleData);
  617. data.push(cdb.Types.Curve.HandleData);
  618. }
  619. }
  620. return cast data;
  621. }
  622. #end
  623. static inline function findCurve(curves: Array<Curve>, suffix: String) {
  624. return curves.find(c -> StringTools.endsWith(c.name, suffix));
  625. }
  626. public static function getVectorValue(curves: Array<Curve>, defVal: Float=0.0, scale: Float=1.0, randomValue: Float = 0) : Value {
  627. inline function find(s) {
  628. return findCurve(curves, s);
  629. }
  630. var x = find(":x");
  631. var y = find(":y");
  632. var z = find(":z");
  633. var w = find(":w");
  634. inline function curveOrVal(c: Curve, defVal: Float) : Value {
  635. if (c == null)
  636. return VConst(defVal);
  637. var vc = c.makeVal();
  638. if (scale != 1.0) {
  639. return VMult(vc, VConst(scale));
  640. }
  641. return vc;
  642. }
  643. return VVector(
  644. curveOrVal(x, defVal),
  645. curveOrVal(y, defVal),
  646. curveOrVal(z, defVal),
  647. curveOrVal(w, 1.0));
  648. }
  649. public static function getColorValue(curves: Array<Curve>) : Value {
  650. inline function find(s) {
  651. return findCurve(curves, s);
  652. }
  653. var r = find(":r");
  654. var g = find(":g");
  655. var b = find(":b");
  656. var a = find(":a");
  657. var h = find(":h");
  658. var s = find(":s");
  659. var l = find(":l");
  660. if(h != null || s != null || l != null) {
  661. return VHsl(
  662. h != null ? h.makeVal() : VConst(0.0),
  663. s != null ? s.makeVal() : VConst(1.0),
  664. l != null ? l.makeVal() : VConst(1.0),
  665. a != null ? a.makeVal() : VConst(1.0));
  666. }
  667. if(a != null && r == null && g == null && b == null)
  668. return a.makeVal();
  669. if(a == null && r == null && g == null && b == null)
  670. return VOne; // White by default
  671. return VVector(
  672. r != null ? r.makeVal() : VConst(1.0),
  673. g != null ? g.makeVal() : VConst(1.0),
  674. b != null ? b.makeVal() : VConst(1.0),
  675. a != null ? a.makeVal() : VConst(1.0));
  676. }
  677. static var _ = Prefab.register("curve", Curve);
  678. }