// This demonstrates animating a two-dimensional graph using interval // arithmetic techniques. These techniques are deep and subtle and amazingly // powerful. They allow you to easily graph arbitrary equations that would be // very tricky otherwise. // // See: https://frinklang.org/#IntervalArithmetic // // For other examples of very simple interval graphing, see: // // https://frinklang.org/fsp/colorize.fsp?f=simplegraph.frink // https://frinklang.org/fsp/colorize.fsp?f=simplegraph3.frink // // Interactive web-based grapher: // https://frinklang.org/fsp/simplegraph.fsp g = new graphics win = g.show[] showApproximations[false] a = new Animation[10/s] for z = .2 to 0 step -.01 { eqStr = "(x^2 + y^2 - 1)^3 - x^2 y^3 = " + format[z,1,3] eqStr2 = eqStr // There's a lot of magic in the "Possibly-Equals" (PEQ) operator! eqStr2 =~ %s/=/PEQ/g eq = parseToExpression[eqStr2] g = new graphics g.color[1,0,0] // Change the last number to vary the resolution. This is the number // of doublings, so if the number is 10 we have 2^10=1024 doublings for // a resolution of 1024x1024. testRect[-3/2,3/2,-3/2,3/2, g, eq, 7] g.font["Serif","italic",.1] g.color[0,0,0] g.text[eqStr,0,-1/4] if (z == 0) { g.font["SansSerif", "bold", .15] g.text["Frink loves you.", 0, 0] } a.add[g] win.replaceGraphics[g] } a.write["vd.gif",400,400] // Recursive function to test an interval containing the specified bounds. // If no possible solution exists, the recursion halts. If a possible solution // exists, this breaks it down into 4 sub-rectangles and tests each of them // recursively. level is the maximum number of levels to split, so the total // resolution of the final graph will be 2^level. testRect[x1, x2, y1, y2, g, eq, level] := { nextLevel = level - 1 x = new interval[x1, x2] y = new interval[y1, y2] // Test the rectangle. If it possibly contains solutions, recursively // subdivide. res = eval[eq] if res or res==undef { if (nextLevel >= 0) { cx = (x1 + x2)/2 cy = (y1 + y2)/2 testRect[x1, cx, y1, cy, g, eq, nextLevel] testRect[cx, x2, y1, cy, g, eq, nextLevel] testRect[x1, cx, cy, y2, g, eq, nextLevel] testRect[cx, x2, cy, y2, g, eq, nextLevel] } else if (res) // Valid point g.fillRectSides[x1, -y1, x2, -y2] else { // Error in evaluating point g.color[1,0,0] g.fillRectSides[x1, -y1, x2, -y2] g.color[0,0,0] } } }