{"id":224,"date":"2010-03-12T16:07:42","date_gmt":"2010-03-12T14:07:42","guid":{"rendered":"http:\/\/labs.miaumiau.cat\/?p=224"},"modified":"2011-07-29T14:42:38","modified_gmt":"2011-07-29T14:42:38","slug":"beziers-our-approach","status":"publish","type":"post","link":"https:\/\/www.miaumiau.cat\/?p=224","title":{"rendered":"Beziers (our approach&#8230;!)"},"content":{"rendered":"<p><a title=\"Beziers\" href=\"http:\/\/miaumiau.cat\/examples\/bezier\/bezierCurves.swf\" rel=\"shadowbox;width=1000;height=600\" target=\"_blank\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-225\" title=\"Imagen 4\" src=\"http:\/\/www.miaumiau.cat\/wp-content\/uploads\/2010\/03\/Imagen-4.png\" alt=\"Beziers\" width=\"515\" height=\"309\" srcset=\"https:\/\/www.miaumiau.cat\/wp-content\/uploads\/2010\/03\/Imagen-4.png 515w, https:\/\/www.miaumiau.cat\/wp-content\/uploads\/2010\/03\/Imagen-4-300x180.png 300w\" sizes=\"auto, (max-width: 515px) 100vw, 515px\" \/><\/a><\/p>\n<p>Bezier curves are widely used in Flash for many effects, from tweening one property to drawing complex curves and surfaces, but we \u00a0have never found one Class that would allow us to work with them the way we wanted to.<\/p>\n<p>Our main goal writing one Class for Bezier manipulation is to have one tool that calculate Bezier interpolations for curves and surfaces of any degree, so the first thing to do was to study the mathematical model of the Bezier curves in these useful\u00a0<a href=\"http:\/\/en.wikipedia.org\/wiki\/B\u00e9zier_curve\">article<\/a>. In the article the Bezier curves are treated as a combination of the Berstain polinomials which uses the factorial function that requires many calculations for high degrees.<\/p>\n<p>The previous problem is solved calculating the coefficients of the curve for the given control points and saving them on a buffer Array that helps us to speed up the calculation process for further interpolations. As the article suggests the interpolation of any curve is given in the [0 &#8211; 1] domain.<\/p>\n<p>The class itself is this one:<br \/>\n[as]<br \/>\npackage math {<br \/>\nimport flash.geom.Vector3D;<\/p>\n<p>\/**<br \/>\n* @author miaumiau.cat<br \/>\n* Clase que se encarga de generar una parametrizaci\u00f3n<br \/>\n* Bezier de trayectoria o superficie.<br \/>\n*<br \/>\n*\/<br \/>\npublic class Bezier {<\/p>\n<p>\/\/Variable que determina si se trabaja en 3D&#8230;<br \/>\npublic static var _3D : Boolean = false;<\/p>\n<p>\/\/Variable que permite tener los datos de la combinatoria para cada grado&#8230;<br \/>\nprivate static var combinatoriaData : Array = new Array();<br \/>\ncombinatoriaData[0] = [0];<br \/>\ncombinatoriaData[1] = [1];<\/p>\n<p>\/\/A part\u00edr de 2 se tienen la cantidad m\u00ednima de puntos para interpolar<br \/>\ncombinatoriaData[2] = new Array(1, 1);<br \/>\ncombinatoriaData[3] = new Array(1, 2, 1);<br \/>\ncombinatoriaData[4] = new Array(1, 3, 3, 1);<br \/>\ncombinatoriaData[5] = new Array(1, 4, 6, 4, 1);<br \/>\ncombinatoriaData[6] = new Array(1, 5, 10, 10, 5, 1);<br \/>\ncombinatoriaData[7] = new Array(1, 6, 15, 20, 15, 6, 1);<br \/>\ncombinatoriaData[8] = new Array(1, 7, 21, 35, 35, 21, 7, 1);<br \/>\ncombinatoriaData[9] = new Array(1, 8, 28, 56, 70, 56, 28, 8, 1);<br \/>\ncombinatoriaData[10] = new Array(1, 9, 36, 84, 126, 126, 84, 36, 9, 1);<br \/>\ncombinatoriaData[11] = new Array(1, 10, 45, 120, 210, 252, 210, 120, 45, 10, 1);<br \/>\ncombinatoriaData[12] = new Array(1, 11, 56, 165, 330, 462, 462, 330, 165, 56, 11, 1);<\/p>\n<p>\/\/Variables est\u00e1ticas que permiten guardar los valores de segmentaci\u00f3n de la malla (evita recalcular la cantidad de puntos&#8230;)<br \/>\nprivate static var Ne : uint = 0;<br \/>\nprivate static var Nn : uint = 0;<br \/>\nprivate static var paramsE : Array = new Array();<br \/>\nprivate static var paramsN : Array = new Array();<\/p>\n<p>\/\/Variable que contiene las constantes de la curva bezier para un grado y una parametrizaci\u00f3n (cantidad de puntos) fija.<br \/>\nprivate static var coeficientesCurva : Array = new Array();<\/p>\n<p>\/\/Funci\u00f3n que calcula los coeficientes de la curva&#8230;<br \/>\npublic static function bezier(t : Number, controlPoints : Vector.) : Vector3D {<\/p>\n<p>var salida : Vector3D = new Vector3D;<br \/>\nvar coeficientes : Array = new Array();<br \/>\nvar i : uint;<br \/>\nvar length : uint = controlPoints.length;<br \/>\nvar n : uint = length &#8211; 1;<\/p>\n<p>\/\/Si los valores de la combinatoria para la cantidad de puntos no estan definidos, los defino&#8230;<br \/>\nif(combinatoriaData[length] == undefined) {<br \/>\ncombinatoriaData[length] = setCoeficients(n);<br \/>\n}<\/p>\n<p>\/\/Determino los coeficientes&#8230;<br \/>\nfor (i = 0;i &lt;= n; i++) {<br \/>\ncoeficientes[i] = combinatoriaData[length][i] * Math.pow(t, i) * Math.pow((1 &#8211; t), (n &#8211; i));<br \/>\n}<\/p>\n<p>\/\/Obtengo los valores de salida del vector3D&#8230;<br \/>\nfor(i = 0;i &lt;= n; i++) {<br \/>\nsalida.x += coeficientes[i] * controlPoints[i].x;<br \/>\nsalida.y += coeficientes[i] * controlPoints[i].y;<br \/>\nsalida.z += coeficientes[i] * controlPoints[i].z;<br \/>\n}<br \/>\nsalida.w = 1;<\/p>\n<p>return salida;<br \/>\n}<\/p>\n<p>\/\/Funci\u00f3n que se encarga de obtener un conjunto de puntos xyz agrupados en un array para una curva bezier&#8230;<br \/>\npublic static function bezierPoints(m : uint, controlPoints : Array, borderDistance : Number = -1) : Vector. {<br \/>\nvar salida : Vector. = new Vector.();<br \/>\nvar length1 : uint = controlPoints.length;<br \/>\nvar n : uint = controlPoints.length &#8211; 1;<br \/>\nvar i : uint;<br \/>\nvar j : uint;<\/p>\n<p>\/\/Si los valores de la combinatoria para la cantidad de puntos no estan definidos, los defino&#8230;<br \/>\nif(combinatoriaData[length1] == undefined) {<br \/>\ncombinatoriaData[length1] = setCoeficients(n);<br \/>\n}<\/p>\n<p>\/\/Si no hay un arreglo que guarde la referencia para un grado definido, se define&#8230;<br \/>\nif(coeficientesCurva[n] == undefined) {<br \/>\ncoeficientesCurva[n] = new Array();<br \/>\n}<\/p>\n<p>\/\/Si no hay un arreglo que guarde los coeficientes para &#8220;m&#8221; puntos se define&#8230;<br \/>\n\/\/Se guarda un arreglo de cuatro dimensiones seg\u00fan la siguiente definici\u00f3n&#8230;<br \/>\n\/\/<br \/>\n\/\/coeficientes[n][m][j][i] donde:<br \/>\n\/\/n : grado,<br \/>\n\/\/m : cantidad de puntos a parametrizar,<br \/>\n\/\/j : vector de coeficientes para un valor de parametrizaci\u00f3n perteneciente al rango [0, 1]<br \/>\n\/\/i : coeficientes a multiplicar por cada punto para la parametrizaci\u00f3n anterior&#8230;<br \/>\n\/\/<\/p>\n<p>if(coeficientesCurva[n][m] == undefined) {<br \/>\ncoeficientesCurva[n][m] = new Array();<br \/>\nfor (j = 0;j &lt; m; j++) {<br \/>\n\/\/Defino la parametrizaci\u00f3n&#8230;<br \/>\nvar delta : Number = j \/ (m &#8211; 1);<br \/>\ncoeficientesCurva[n][m][j] = new Array();<br \/>\nfor(i = 0;i &lt;= n; i++) {<br \/>\ncoeficientesCurva[n][m][j].push(combinatoriaData[length1][i] * Math.pow(delta, i) * Math.pow((1 &#8211; delta), (n &#8211; i)));<br \/>\n}<br \/>\n}<br \/>\n}<\/p>\n<p>\/\/Obtengo los distintos puntos que componen el vector de salida&#8230;<br \/>\nfor(j = 0;j &lt; m; j++) {<br \/>\nvar pointer : Vector3D = new Vector3D(0, 0, 0, 0);<br \/>\nfor(i = 0;i &lt;= n; i++) { pointer.x += coeficientesCurva[n][m][j][i] * controlPoints[i].x; pointer.y += coeficientesCurva[n][m][j][i] * controlPoints[i].y; pointer.z += coeficientesCurva[n][m][j][i] * controlPoints[i].z; } salida.push(pointer); } \/\/En caso de requerir bordes fijos&#8230; if(borderDistance &gt; 0) {<br \/>\n\/\/Determino la longitud de la curva y calculo el valor porcentual de la parametrizaci\u00f3n de 0 a 1<br \/>\nvar length : Number = 0;<br \/>\nfor(i = 1;i &lt; salida.length; i++) {<br \/>\nlength += Vector3D.distance(salida[i], salida[i &#8211; 1]);<br \/>\n}<br \/>\nvar percentDistance : Number = borderDistance \/ length;<br \/>\nvar centerDistance : Number = (1 &#8211; 2 * percentDistance) \/ (m &#8211; 3);<br \/>\nvar parameters : Array = new Array();<br \/>\nvar relativeCoeficients : Array = new Array();<br \/>\nparameters.push(0);<br \/>\nfor(i = 0;i &lt; m &#8211; 2; i++) {<br \/>\nparameters.push(percentDistance + i * centerDistance);<br \/>\n}<br \/>\nparameters.push(1);<br \/>\n\/\/Determino el nuevo grupo de coeficientes a utilizar dependiendo de la parametrizaci\u00f3n&#8230;<br \/>\nfor (j = 0;j &lt; m; j++) {<br \/>\nrelativeCoeficients.push(new Array());<br \/>\nfor(i = 0;i &lt;= n; i++) {<br \/>\nrelativeCoeficients[j].push(combinatoriaData[controlPoints.length][i] * Math.pow(parameters[j], i) * Math.pow((1 &#8211; parameters[j]), (n &#8211; i)));<br \/>\n}<br \/>\n}<br \/>\n\/\/Obtengo los nuevos puntos tomando en cuenta las separaciones requeridas&#8230;<br \/>\nsalida.length = 0;<br \/>\nfor(j = 0;j &lt; m; j++) {<br \/>\nvar pointer1 : Vector3D = new Vector3D(0, 0, 0, 0);<br \/>\nfor(i = 0;i &lt;= n; i++) {<br \/>\npointer1.x += relativeCoeficients[j][i] * controlPoints[i].x;<br \/>\npointer1.y += relativeCoeficients[j][i] * controlPoints[i].y;<br \/>\nif(_3D) pointer.z += relativeCoeficients[j][i] * controlPoints[i].z;<br \/>\n}<br \/>\nsalida.push(pointer1);<br \/>\n}<br \/>\n}<\/p>\n<p>return salida;<br \/>\n}<\/p>\n<p>\/\/Funci\u00f3n que devuelve una superficie Bezier de MxN puntos de control (de borde e internos), se diferencia del patch porque esta \u00faltima solo permite el control con las curvas de borde (no hay puntos internos&#8230;)<br \/>\npublic static function surface(u_cps : uint, points : Vector., Ne : uint = 10, Nn : uint = 10, force : uint = 1) : Vector. {<\/p>\n<p>var i : uint;<br \/>\nvar j : uint;<br \/>\nvar k : uint;<br \/>\nvar r : uint;<br \/>\nvar output : Vector. = new Vector.();<br \/>\nvar v_cps : uint = points.length \/ u_cps;<br \/>\nvar cp_curves : Array = new Array();<\/p>\n<p>\/\/Separo los puntos para obtener cada curva&#8230;<br \/>\nfor(i = 0; i &lt; u_cps; i++) {<br \/>\ncp_curves[i] = new Vector.();<br \/>\nfor(j = i; j &lt;= v_cps * (u_cps &#8211; 1) + i; j += u_cps) {<br \/>\nfor(r = 0; r &lt; force; r ++) {<br \/>\ncp_curves[i].push(points[j]);<br \/>\n}<br \/>\n}<br \/>\n}<\/p>\n<p>\/\/Genero los par\u00e1metros si no estan definidos&#8230;<br \/>\nif(Bezier.Ne != Ne &amp;&amp; Bezier.Nn != Ne) {<br \/>\nBezier.Ne = Ne;<br \/>\nBezier.Nn = Nn;<br \/>\nparamsN = [];<br \/>\nparamsE = [];<br \/>\nfor (i = 1;i &lt;= Nn; i++) {<br \/>\nparamsN.push((i &#8211; 1) \/ (Nn &#8211; 1));<br \/>\n}<\/p>\n<p>for (i = 1;i &lt;= Ne; i++) {<br \/>\nparamsE.push((i &#8211; 1) \/ (Ne &#8211; 1));<br \/>\n}<br \/>\n}<\/p>\n<p>\/\/Genero el arreglo de puntos&#8230;<br \/>\nvar jMax : uint = paramsN.length;<br \/>\nvar iMax : uint = paramsE.length;<\/p>\n<p>for (j = 0;j &lt; jMax; j++) {<\/p>\n<p>\/\/Conjunto de puntos obtenidos de evaluar las curvas verticales&#8230;<br \/>\nvar resultant_curve : Vector. = new Vector.();<br \/>\nfor(k = 0; k &lt; cp_curves.length; k++) {<br \/>\nfor(r = 0; r &lt; force; r++) {<br \/>\nresultant_curve.push(bezier(paramsN[j], cp_curves[k]));<br \/>\n}<br \/>\n}<\/p>\n<p>\/\/De los puntos obtenidos de las curvas verticales se obtiene una curva horizontal que al ser evaluada entrega cada punto de la superficie&#8230;<br \/>\nfor (i = 0;i &lt; iMax; i++) {<br \/>\noutput.push(bezier(paramsE[i], resultant_curve));<br \/>\n}<br \/>\n}<br \/>\nreturn output;<br \/>\n}<\/p>\n<p>\/\/Funci\u00f3n que se encarga de conseguir todos los puntos de una malla generada por bordes&#8230;<br \/>\n\/\/Entrega los puntos ordenados de la siguiente manera suponiendo un arreglo de 3X3&#8230;<br \/>\n\/\/<br \/>\n\/\/ 0 1 2<br \/>\n\/\/ 3 4 5<br \/>\n\/\/ 6 7 8<br \/>\n\/\/<\/p>\n<p>public static function getPatch(Xt0 : Vector3D, Xt1 : Vector3D, Xb0 : Vector3D, Xb1 : Vector3D, BT : Array, BB : Array, BL : Array, BR : Array, Ne : uint = 10, Nn : uint = 10, borderSeparation : Number = -1) : Array {<\/p>\n<p>var i : uint;<br \/>\nvar points : Array = new Array();<\/p>\n<p>\/\/Obtengo los puntos de las curvas para interpolar&#8230;<br \/>\nvar b_top : Vector. = Bezier.bezierPoints(Ne, BT, borderSeparation);<br \/>\nvar b_bottom : Vector. = Bezier.bezierPoints(Ne, BB, borderSeparation);<br \/>\nvar b_left : Vector. = Bezier.bezierPoints(Nn, BL, borderSeparation);<br \/>\nvar b_right : Vector. = Bezier.bezierPoints(Nn, BR, borderSeparation);<\/p>\n<p>\/\/Genero los par\u00e1metros si no estan definidos&#8230;<br \/>\nif(Bezier.Ne != Ne &amp;&amp; Bezier.Nn != Ne) {<br \/>\nBezier.Ne = Ne;<br \/>\nBezier.Nn = Nn;<br \/>\nparamsN = [];<br \/>\nparamsE = [];<br \/>\nfor (i = 1;i &lt;= Nn; i++) {<br \/>\nparamsN.push((i &#8211; 1) \/ (Nn &#8211; 1));<br \/>\n}<\/p>\n<p>for (i = 1;i &lt;= Ne; i++) {<br \/>\nparamsE.push((i &#8211; 1) \/ (Ne &#8211; 1));<br \/>\n}<br \/>\n}<\/p>\n<p>\/\/Genero el arreglo de puntos&#8230;<br \/>\nvar j : uint;<br \/>\nvar jMax : uint = paramsN.length;<br \/>\nvar iMax : uint = paramsE.length;<br \/>\nfor (i = 0;i &lt; iMax; i++) {<br \/>\nfor (j = 0;j &lt; jMax; j++) {<br \/>\npoints.push(TFI(paramsE[i], paramsN[j], b_top[i], b_bottom[i], b_left[j], b_right[j], Xt0, Xt1, Xb0, Xb1));<br \/>\n}<br \/>\n}<br \/>\nreturn points;<br \/>\n}<\/p>\n<p>\/\/Funci\u00f3n que permite liberar la memoria de la clase Bezier&#8230;<br \/>\npublic static function clearMemory() : void {<br \/>\ncoeficientesCurva = [];<br \/>\nparamsN = [];<br \/>\nparamsE = [];<br \/>\ncombinatoriaData = [];<\/p>\n<p>coeficientesCurva = paramsN = paramsE = combinatoriaData = null;<br \/>\n}<\/p>\n<p>\/\/Funci\u00f3n que genera una interpolaci\u00f3n tranfinita TFI para un grupo de cuatro curvas de borde bezier.<br \/>\n\/\/Se pasan los puntos de borde Xt0, Xt1, Xb0, Xb1 y los valores e, n definidos de 0 a 1 para pasar de<br \/>\n\/\/estado plano al definido por las cuatro curvas&#8230; se busca generar los puntos P intermedios&#8230;<br \/>\n\/\/<br \/>\n\/\/<br \/>\n\/\/ Xt0 Xt Xt Xt Xt Xt Xt Xt1<br \/>\n\/\/ Xl Xr<br \/>\n\/\/ Xl Xr<br \/>\n\/\/ Xl P Xr<br \/>\n\/\/ Xl Xr<br \/>\n\/\/ Xl Xr<br \/>\n\/\/ Xl Xr<br \/>\n\/\/ Xl Xr<br \/>\n\/\/ Xb0 Xb Xb Xb Xb Xb Xb Xb1<br \/>\n\/\/<br \/>\n\/\/<br \/>\nprivate static function TFI(e : Number, n : Number, Xt : Vector3D, Xb : Vector3D, Xl : Vector3D, Xr : Vector3D , Xt0 : Vector3D, Xt1 : Vector3D, Xb0 : Vector3D, Xb1 : Vector3D) : Vector3D {<\/p>\n<p>var TFIPoint : Vector3D = new Vector3D();<\/p>\n<p>\/\/Eval\u00fao la interpolaci\u00f3n para cada coordenada del punto, x, y, z&#8230;<br \/>\nTFIPoint.x = (1 &#8211; n) * Xt.x + n * Xb.x + (1 &#8211; e) * Xl.x + e * Xr.x &#8211; (e * n * Xb1.x + e * (1 &#8211; n) * Xt1.x + n * (1 &#8211; e) * Xb0.x + (1 &#8211; n) * (1 &#8211; e) * Xt0.x);<br \/>\nTFIPoint.y = (1 &#8211; n) * Xt.y + n * Xb.y + (1 &#8211; e) * Xl.y + e * Xr.y &#8211; (e * n * Xb1.y + e * (1 &#8211; n) * Xt1.y + n * (1 &#8211; e) * Xb0.y + (1 &#8211; n) * (1 &#8211; e) * Xt0.y);<br \/>\nif(_3D) TFIPoint.z = (1 &#8211; n) * Xt.z + n * Xb.z + (1 &#8211; e) * Xl.z + e * Xr.z &#8211; (e * n * Xb1.z + e * (1 &#8211; n) * Xt1.z + n * (1 &#8211; e) * Xb0.z + (1 &#8211; n) * (1 &#8211; e) * Xt0.z);<br \/>\nTFIPoint.w = 1;<br \/>\nreturn TFIPoint;<br \/>\n}<\/p>\n<p>\/\/Funci\u00f3n que realiza un set de los coeficientes en caso que cantidad de puntos sean distintos a los valores almacenados&#8230;<br \/>\nprivate static function setCoeficients(n : Number) : Array {<br \/>\nvar datos : Array = new Array();<br \/>\nvar i : Number;<br \/>\nfor (i = 0;i &lt;= n; i++) {<br \/>\ndatos.push(MathFunctions.combinatoria(n, i));<br \/>\n}<br \/>\nreturn datos;<br \/>\n}<\/p>\n<p>\/\/f\u00edn del programa&#8230;.<br \/>\n}<br \/>\n}<br \/>\n[\/as]<\/p>\n<p>As you can see all the comments are written in Spanish and many functions have so many parameters that is somehow difficult to understand what these functions do, so i\u00b4ll try to explain every function and it\u00b4s implementation in this post and in the near future post. Anyway i\u00b4ll comment some important features of it.<\/p>\n<p>The combinatoriaData Array is a set of coefficients of the Berstain polinomials that are pre-calculated for degrees lower than 12, this helps us to speed the initial calculation for one interpolation. If you are going to work with a higher degree, the program calculates the coefficients for that degree and saves the values on the array.<\/p>\n<p>The bezier function is the simplest function of the class, it allows to interpolate one single 3D point from a set of controlPoints and a parameter value between 0 an 1. You can use this function to get the interpolation point to point.<\/p>\n<p>The bezierPoints function does the same thing but gives all the points of the interpolation in one array, you have to note that the &#8220;m&#8221; value is the number of points that you want to interpolate. \u00a0Since all the points are equidistant in the domain space you can\u00b4t control interpolation by distance from point to point in the parameter space, but there\u00b4s one parameter that allows you to at least control the initial distance between borders and the next \/ previous parameter point, if you would like one margin if you are treating with surfaces (i\u00b4ll promise i\u00b4ll make one example of this, because it\u00b4s difficult to understand just by reading it).<\/p>\n<p>The surface function does the same thins that Away3D\u00b4s bezier surface function except that you can define the MxN control points as you want (not just 4X4 surface). To use the function you have to pass the M dimension, the Vector with the MxN points and the segmentation for the U,V direction of the surface. The last parameter, the &#8220;force&#8221; is the repetition of the control points in the surface to simulate weights \u00a0for a given controlPoints, this makes the surface approximate more to the controlPoints resulting more like a Nurbs curve with the same controlPoints.\u00a0The previos function is used to calculate the surface in this <a href=\"http:\/\/labs.miaumiau.cat\/?p=178\">post<\/a> .<\/p>\n<p>The getPatch function also returns a surface, but this surface has the property of being controlled only by the curves that defines the borders of the surface, it means that the internal values of the surface are only border dependant and there are no internal controlPoints for this surface. This function is more complicated to use, but is more versatil in some applications like transitions and deformations.<br \/>\nFinally we have written one simple implementation of this class using only the fist function &#8220;Bezier.bezier()&#8221;. If you want to see it, just click on the image in above and relax!.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Bezier curves are widely used in Flash for many effects, from tweening one property to drawing complex curves and surfaces, but we \u00a0have never found one Class that would allow us to work with them the way we wanted to. Our main goal writing one Class for Bezier manipulation is to have one tool that [&hellip;]<\/p>\n","protected":false},"author":3,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-224","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/www.miaumiau.cat\/index.php?rest_route=\/wp\/v2\/posts\/224","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.miaumiau.cat\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.miaumiau.cat\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.miaumiau.cat\/index.php?rest_route=\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/www.miaumiau.cat\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=224"}],"version-history":[{"count":2,"href":"https:\/\/www.miaumiau.cat\/index.php?rest_route=\/wp\/v2\/posts\/224\/revisions"}],"predecessor-version":[{"id":671,"href":"https:\/\/www.miaumiau.cat\/index.php?rest_route=\/wp\/v2\/posts\/224\/revisions\/671"}],"wp:attachment":[{"href":"https:\/\/www.miaumiau.cat\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=224"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.miaumiau.cat\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=224"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.miaumiau.cat\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=224"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}