2
0

Curve.hx 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  1. package hrt.prefab;
  2. using Lambda;
  3. class CurveHandle {
  4. public var dt: Float;
  5. public var dv: Float;
  6. public function new(t, v) {
  7. this.dt = t;
  8. this.dv = v;
  9. }
  10. }
  11. enum abstract CurveKeyMode(Int) {
  12. var Aligned = 0;
  13. var Free = 1;
  14. var Linear = 2;
  15. var Constant = 3;
  16. }
  17. class CurveKey {
  18. public var time: Float;
  19. public var value: Float;
  20. public var mode: CurveKeyMode;
  21. public var prevHandle: CurveHandle;
  22. public var nextHandle: CurveHandle;
  23. public function new() {}
  24. }
  25. typedef CurveKeys = Array<CurveKey>;
  26. class Curve extends Prefab {
  27. @:s public var keyMode : CurveKeyMode = Linear;
  28. @:c public var keys : CurveKeys = [];
  29. @:c public var previewKeys : CurveKeys = [];
  30. @:s public var loop : Bool = false;
  31. public var maxTime : Float;
  32. public var duration(get, never): Float;
  33. function get_duration() {
  34. if(keys.length == 0) return 0.0;
  35. return keys[keys.length-1].time;
  36. }
  37. public function new(?parent) {
  38. super(parent);
  39. this.type = "curve";
  40. }
  41. public override function load(o:Dynamic) {
  42. super.load(o);
  43. keys = [];
  44. if(o.keys != null) {
  45. for(k in (o.keys: Array<Dynamic>)) {
  46. var nk = new CurveKey();
  47. nk.time = k.time;
  48. nk.value = k.value;
  49. nk.mode = k.mode;
  50. if(k.prevHandle != null)
  51. nk.prevHandle = new CurveHandle(k.prevHandle.dt, k.prevHandle.dv);
  52. if(k.nextHandle != null)
  53. nk.nextHandle = new CurveHandle(k.nextHandle.dt, k.nextHandle.dv);
  54. keys.push(nk);
  55. }
  56. }
  57. if( keys.length == 0 ) {
  58. addKey(0.0, 0.0);
  59. addKey(1.0, 1.0);
  60. }
  61. }
  62. public override function save() {
  63. var obj : Dynamic = super.save();
  64. var keysDat = [];
  65. for(k in keys) {
  66. var o = {
  67. time: k.time,
  68. value: k.value,
  69. mode: k.mode
  70. };
  71. if(k.prevHandle != null) Reflect.setField(o, "prevHandle", { dv: k.prevHandle.dv, dt: k.prevHandle.dt });
  72. if(k.nextHandle != null) Reflect.setField(o, "nextHandle", { dv: k.nextHandle.dv, dt: k.nextHandle.dt });
  73. keysDat.push(o);
  74. }
  75. obj.keys = keysDat;
  76. return obj;
  77. }
  78. static inline function bezier(c0: Float, c1:Float, c2:Float, c3: Float, t:Float) {
  79. var u = 1 - t;
  80. return u * u * u * c0 + c1 * 3 * t * u * u + c2 * 3 * t * t * u + t * t * t * c3;
  81. }
  82. public function findKey(time: Float, tolerance: Float) {
  83. var minDist = tolerance;
  84. var closest = null;
  85. for(k in keys) {
  86. var d = hxd.Math.abs(k.time - time);
  87. if(d < minDist) {
  88. minDist = d;
  89. closest = k;
  90. }
  91. }
  92. return closest;
  93. }
  94. public function addKey(time: Float, ?val: Float, ?mode=null) {
  95. var index = 0;
  96. for(ik in 0...keys.length) {
  97. var key = keys[ik];
  98. if(time > key.time)
  99. index = ik + 1;
  100. }
  101. if(val == null)
  102. val = getVal(time);
  103. var key = new hrt.prefab.Curve.CurveKey();
  104. key.time = time;
  105. key.value = val;
  106. key.mode = mode != null ? mode : (keys[index] != null ? keys[index].mode : keyMode);
  107. keys.insert(index, key);
  108. return key;
  109. }
  110. public function addPreviewKey(time: Float, val: Float) {
  111. var key = new hrt.prefab.Curve.CurveKey();
  112. key.time = time;
  113. key.value = val;
  114. previewKeys.push(key);
  115. return key;
  116. }
  117. public function getBounds() {
  118. // TODO: Take bezier handles into account
  119. var ret = new h2d.col.Bounds();
  120. for(k in keys) {
  121. ret.addPos(k.time, k.value);
  122. }
  123. return ret;
  124. }
  125. public function getVal(time: Float) : Float {
  126. switch(keys.length) {
  127. case 0: return 0;
  128. case 1: return keys[0].value;
  129. default:
  130. }
  131. if (loop)
  132. time = time % keys[keys.length-1].time;
  133. var idx = -1;
  134. for(ik in 0...keys.length) {
  135. var key = keys[ik];
  136. if(time > key.time)
  137. idx = ik;
  138. }
  139. if(idx < 0)
  140. return keys[0].value;
  141. var cur = keys[idx];
  142. var next = keys[idx + 1];
  143. if(next == null || cur.mode == Constant)
  144. return cur.value;
  145. var minT = 0.;
  146. var maxT = 1.;
  147. var maxDelta = 1./ 25.;
  148. inline function sampleTime(t) {
  149. return bezier(
  150. cur.time,
  151. cur.time + (cur.nextHandle != null ? cur.nextHandle.dt : 0.),
  152. next.time + (next.prevHandle != null ? next.prevHandle.dt : 0.),
  153. next.time, t);
  154. }
  155. inline function sampleVal(t) {
  156. return bezier(
  157. cur.value,
  158. cur.value + (cur.nextHandle != null ? cur.nextHandle.dv : 0.),
  159. next.value + (next.prevHandle != null ? next.prevHandle.dv : 0.),
  160. next.value, t);
  161. }
  162. while( maxT - minT > maxDelta ) {
  163. var t = (maxT + minT) * 0.5;
  164. var x = sampleTime(t);
  165. if( x > time )
  166. maxT = t;
  167. else
  168. minT = t;
  169. }
  170. var x0 = sampleTime(minT);
  171. var x1 = sampleTime(maxT);
  172. var dx = x1 - x0;
  173. var xfactor = dx == 0 ? 0.5 : (time - x0) / dx;
  174. var y0 = sampleVal(minT);
  175. var y1 = sampleVal(maxT);
  176. var y = y0 + (y1 - y0) * xfactor;
  177. return y;
  178. }
  179. public function getSum(time: Float) : Float {
  180. var duration = keys[keys.length-1].time;
  181. if(loop && time > duration) {
  182. var cycles = Math.floor(time / duration);
  183. return getSum(duration) * cycles + getSum(time - cycles);
  184. }
  185. var sum = 0.0;
  186. for(ik in 0...keys.length) {
  187. var key = keys[ik];
  188. if(time < key.time)
  189. break;
  190. if(ik == 0 && key.time > 0) {
  191. // Account for start of curve
  192. sum += key.time * key.value;
  193. }
  194. var nkey = keys[ik + 1];
  195. if(nkey != null) {
  196. if(time > nkey.time) {
  197. // Full interval
  198. sum += key.value * (nkey.time - key.time);
  199. if(key.mode != Constant)
  200. sum += 0.5 * (nkey.time - key.time) * (nkey.value - key.value);
  201. }
  202. else {
  203. // Split interval
  204. sum += key.value * (time - key.time);
  205. if(key.mode != Constant)
  206. sum += 0.5 * (time - key.time) * hxd.Math.lerp(key.value, nkey.value, (time - key.time) / (nkey.time - key.time));
  207. }
  208. }
  209. else {
  210. sum += key.value * (time - key.time);
  211. }
  212. }
  213. return sum;
  214. }
  215. public function sample(numPts: Int) {
  216. var vals = [];
  217. var duration = this.duration;
  218. for(i in 0...numPts) {
  219. var v = getVal(duration * i/(numPts-1));
  220. vals.push(v);
  221. }
  222. return vals;
  223. }
  224. #if editor
  225. override function edit( ctx : EditContext ) {
  226. super.edit(ctx);
  227. ctx.properties.add(new hide.Element('
  228. <div class="group" name="Parameters">
  229. <dl>
  230. <dt>Loop curve</dt><dd><input type="checkbox" field="loop"/></dd>
  231. </dl>
  232. </div>'), this, function(pname) {
  233. ctx.onChange(this, pname);
  234. });
  235. var ce = new hide.comp.CurveEditor(ctx.properties.undo, ctx.properties.element);
  236. ce.curve = this;
  237. }
  238. override function getHideProps() : HideProps {
  239. return { icon : "paint-brush", name : "Curve" };
  240. }
  241. #end
  242. public static function getCurve(parent : Prefab, name: String, onlyEnabled=true) {
  243. for(c in parent.children) {
  244. if(onlyEnabled && !c.enabled) continue;
  245. if(c.name != name) continue;
  246. var curve = c.to(Curve);
  247. if(curve == null) continue;
  248. return curve;
  249. }
  250. return null;
  251. }
  252. public static function getCurves(parent: Prefab, prefix: String) {
  253. var ret = null;
  254. for(c in parent.children) {
  255. if(!c.enabled) continue;
  256. var idx = c.name.indexOf(".");
  257. var curvePrefix = (idx >= 0) ? c.name.substr(0, idx) : c.name;
  258. if(curvePrefix != prefix)
  259. continue;
  260. var curve = c.to(Curve);
  261. if(curve == null) continue;
  262. if (ret == null) ret = [];
  263. ret.push(curve);
  264. }
  265. return ret;
  266. }
  267. public static function getGroups(curves: Array<Curve>) {
  268. var groups : Array<{name: String, items: Array<Curve>}> = [];
  269. for(c in curves) {
  270. var prefix = c.name.split(".")[0];
  271. var g = groups.find(g -> g.name == prefix);
  272. if(g == null) {
  273. groups.push({
  274. name: prefix,
  275. items: [c]
  276. });
  277. }
  278. else {
  279. g.items.push(c);
  280. }
  281. }
  282. return groups;
  283. }
  284. static inline function findCurve(curves: Array<Curve>, suffix: String) {
  285. return curves.find(c -> StringTools.endsWith(c.name, suffix));
  286. }
  287. public static function getVectorValue(curves: Array<Curve>, defVal: Float=0.0, scale: Float=1.0) : hrt.prefab.fx.Value {
  288. inline function find(s) {
  289. return findCurve(curves, s);
  290. }
  291. var x = find(".x");
  292. var y = find(".y");
  293. var z = find(".z");
  294. var w = find(".w");
  295. inline function curveOrVal(c: Curve, defVal: Float) : hrt.prefab.fx.Value {
  296. return c != null ? (scale != 1.0 ? VCurveScale(c, scale) : VCurve(c)) : VConst(defVal);
  297. }
  298. return VVector(
  299. curveOrVal(x, defVal),
  300. curveOrVal(y, defVal),
  301. curveOrVal(z, defVal),
  302. curveOrVal(w, 1.0));
  303. }
  304. public static function getColorValue(curves: Array<Curve>) : hrt.prefab.fx.Value {
  305. inline function find(s) {
  306. return findCurve(curves, s);
  307. }
  308. var r = find(".r");
  309. var g = find(".g");
  310. var b = find(".b");
  311. var a = find(".a");
  312. var h = find(".h");
  313. var s = find(".s");
  314. var l = find(".l");
  315. if(h != null || s != null || l != null) {
  316. return VHsl(
  317. h != null ? VCurve(h) : VConst(0.0),
  318. s != null ? VCurve(s) : VConst(1.0),
  319. l != null ? VCurve(l) : VConst(1.0),
  320. a != null ? VCurve(a) : VConst(1.0));
  321. }
  322. if(a != null && r == null && g == null && b == null)
  323. return VCurve(a);
  324. if(a == null && r == null && g == null && b == null)
  325. return VOne; // White by default
  326. return VVector(
  327. r != null ? VCurve(r) : VConst(1.0),
  328. g != null ? VCurve(g) : VConst(1.0),
  329. b != null ? VCurve(b) : VConst(1.0),
  330. a != null ? VCurve(a) : VConst(1.0));
  331. }
  332. static var _ = Library.register("curve", Curve);
  333. }