// This is a simple but rather interesting program that graphs equations. // You enter equations in terms of x and y, something like one of the // following: // // y = sin[x] // // x^2 + y^2 = 81 // // y cos[x] = x sin[y] // // This version of the program can also graph INEQUALITIES, which have // less-than or greater-than symbols instead of just equals. // // For example, try // // abs[y^2 + x^4 - 1] < cos[x] // // This uses a recursive method to subdivide and test rectangles. use Grid.frink lasteq = "" // If there are arguments to the program, graph them, otherwise prompt. while func = (length[ARGS] > 0 ? ARGS@0 : input["Enter equation: ", lasteq]) { hasInequality = false certEq = undef lasteq = certFunc = func g = new graphics // If there's an inequality, let's make a test equation to see if we can // plot an entire rectangle using the "CERTAINLY" comparators. if func =~ %r/([<>]|!=)/ { hasInequality = true g.antialiased[false] certFunc =~ %s/<=/ CLE /g // Replace <= with certainly less than or equals certFunc =~ %s/>=/ CGE /g // Replace >= with certainly greater than or equals certFunc =~ %s// CGT /g // Replace > with certainly greater than certFunc =~ %s/!=/ CNE /g // Replace = with certainly not equals certFunc =~ %s/=/ CEQ /g // Replace = with certainly equals certEq = parseToExpression[certFunc] } // These replacements turn normal comparator and equality tests into // "POSSIBLY EQUALS" tests. func =~ %s/<=/ PLE /g // Replace <= with possibly less than or equals func =~ %s/>=/ PGE /g // Replace >= with possibly greater than or equals func =~ %s// PGT /g // Replace > with possibly greater than func =~ %s/!=/ PNE /g // Replace = with possibly not equals func =~ %s/=/ PEQ /g // Replace = with possibly equals eq = parseToExpression[func] println[func] xmin = -20 xmax = 20 ymin = -20 ymax = 20 // 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[xmin, -ymin, xmax, -ymax, g, eq, certEq, 11] grid = new Grid grid.auto[g] g.add[grid.getGrid[]] g.show[] g.write["graph2.png",1024,1024] g.write["graph2.svg",1024,1024] if length[ARGS] > 0 exit[] } // 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, certEq, 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) { if (certEq != undef) // Do we have inequalities and a CERTAINLY test? certRes = eval[certEq] if certRes == true { // If the entire rectangle is a solution, then fill the rectangle // and stop further recursion on this rectangle. g.fillRectSides[x1, -y1, x2, -y2] return } // Further subdivide the rectangle into 4 quadrants and recursively // test them all cx = (x1 + x2)/2 cy = (y1 + y2)/2 testRect[x1, cx, y1, cy, g, eq, certEq, nextLevel] testRect[cx, x2, y1, cy, g, eq, certEq, nextLevel] testRect[x1, cx, cy, y2, g, eq, certEq, nextLevel] testRect[cx, x2, cy, y2, g, eq, certEq, nextLevel] } else if (res) // Valid point g.fillRectSides[x1, -y1, x2, -y2] else { // Error in evaluating point, plot in red. g.color[1,0,0] g.fillRectSides[x1, -y1, x2, -y2] g.color[0,0,0] } } }