smart_arrays_contour_finder 2.0.1 smart_arrays_contour_finder: ^2.0.1 copied to clipboard
This package contours the 3-dimensional surface represented by the values f(x,y) of a matrix. The detected contour lines are passed on to a user-provided renderer.
import 'dart:html';
import 'dart:typed_data';
import 'dart:math' as math;
import 'package:smart_arrays_contour_finder/smart_arrays_contour_finder.dart';
import 'package:smart_arrays_base/smart_arrays_base.dart';
/// Example how to use the [ContourFinder] of the package
/// [smart_arrays_contour_finder]. First, a matrix to be contoured is computed.
/// Then [ContourFinder.findContour] is employed to search for the contours
/// and to graphically display the contours using the provided
/// [SampleContourRenderer]. You can execute this example by downloading the
/// files [example.dart.js] and [example.html], and executing the latter one in
/// your browser.
main() {
// First create a sample matrix to be contoured: f(x,y)=x*sin(y)+y*sin(x)
SampleMatrix sample = SampleMatrix();
// Compute equally spaced contour levels
final int NLEVELS = 12;
MinMax minmax = Array2D.getMinMax(sample.matrix);
double levelDelta = (minmax.maxValue - minmax.minValue).abs() / (NLEVELS - 1);
Float64List levels = Float64List(NLEVELS);
for (int i = 0; i < NLEVELS; i++) {
levels[i] = (minmax.minValue + i * levelDelta) * 0.95;
}
// construct a ContourFinder emitting the found contours to the sample renderer.
ContourFinder contourFinder = ContourFinder(SampleContourRenderer());
// Start contour search and rendering using [ContourRendererSample].
contourFinder.findContour(
sample.matrix, // in this matrix
0, // from this row
sample.matrix.length - 1, // to this row
0, // from this col
sample.matrix[0].length - 1, // to this col
sample.yRowCoordinates, // normalized row coordinates (0...1)
sample.xColCoordinates, // normalized col coordinates (0...1)
levels.length, // number of levels
levels); // contour levels to draw
}
/// A sample countour renderer drawing the contours into the [CanvasElement].
/// defined in [example.html]. Negative contours are drawn in magenta, positive
/// ones in blue.
class SampleContourRenderer implements ContourRenderer {
CanvasRenderingContext2D c2d;
CanvasElement contourCanvas;
int last_x1, last_y1, last_x2, last_y2;
// size and location of drawing area:
int dataAreaWidth, dataAreaHeight, dataAreaX = 0, dataAreaY = 0;
bool rotate90 = true; // optional rotation of contour plot
/// Sets up the graphics 2D context to which drawing will be performed.
SampleContourRenderer() {
Element ccel = document.getElementById("contour_canvas");
contourCanvas = ccel; // cast
contourCanvas.style
..position = "absolute"
..backgroundColor = "transparent";
c2d = contourCanvas.getContext("2d");
dataAreaWidth = contourCanvas.width;
dataAreaHeight = contourCanvas.height;
}
/// Implements the ContourRenderer: draws the specified contour line
/// belonging to [contourLevel] into the graphics 2D context. [contourLevel]
/// is needed to color the line: Here we give a different color to positive
/// and negative levels.
void drawContourLine(double startX, double startY, double endX, double endY,
double contourLevel) {
// Some apps need to rotate the contour by -90 deg. around the data area center
// to obtain the typical contour representation. This code shows how to do that.
// The rows will be drawn from the botton to the top, i.e. the biggest row
// number will be drawn at the top of the data area.
// This code also applies the data area origin, and accounts for the data area
// width/height ratio.
int x1 = (startY * dataAreaWidth).round() + dataAreaX;
int x2 = (endY * dataAreaWidth).round() + dataAreaX;
int y1 = ((1 - startX) * dataAreaHeight).round() + dataAreaY;
int y2 = ((1 - endX) * dataAreaHeight).round() + dataAreaY;
// skip lines that don't change the plot.
// This speeds up drawing as long as we don't use compressed data.
if (x1 == x2 && y1 == y2) {
return;
}
if (last_x1 != null) {
// would overlap last line
if ((last_x1 == x1 && last_x2 == x2) &&
(last_y1 == y1 && last_y2 == y2)) {
return;
}
}
last_x1 = x1;
last_x2 = x2;
last_y1 = y1;
last_y2 = y2;
// Draw directly into canvas
c2d.beginPath();
c2d.moveTo(x1, y1);
c2d.lineTo(x2, y2);
if (contourLevel < 0) {
c2d.strokeStyle = "magenta";
} else {
c2d.strokeStyle = "blue";
}
c2d.lineWidth = 1;
c2d.stroke();
}
}
/// A sample matrix: f(x,y)=x*sin(y)+y*sin(x) in the range -3*pi < x,y < 3*pi.
class SampleMatrix {
static final int NROWS = 400, NCOLS = 600;
static final double xstart = 3 * math.pi, ystart = 3 * math.pi;
List<Float64List> matrix = List<Float64List>(NROWS); // the matrix
Float64List xColCoordinates = Float64List(NCOLS); // its col coordinates
Float64List yRowCoordinates = Float64List(NROWS); // its row coordinates
SampleMatrix() {
double x, y;
for (int i = 0; i < NROWS; i++) {
matrix[i] = Float64List(NCOLS);
x = xstart * (-1.0 + 2 * i / (NROWS - 1));
yRowCoordinates[i] = i / (NROWS - 1); // normalize to 0.0 ... 1.0
for (int k = 0; k < NCOLS; k++) {
y = ystart * (-1.0 + 2 * k / (NCOLS - 1));
matrix[i][k] = x * math.sin(y) - y * math.cos(x);
xColCoordinates[k] = k / (NCOLS - 1); // normalize to 0.0 ... 1.0
}
}
}
}