Skip to main content
Search IntMath
Close

Home | Math Display Experiments | svgPHPGrapher Syntax Examples

Page by Murray Bourne, IntMath.com. Last updated: 07 Jul 2019

svgPHPGrapher Syntax Examples

svgPHPGrapher is a PHP-based mathematical grapher that produces svg images. It's a highly-modified PHP port of ASCIIsvg.js by Peter Jipsen.

The advantages of producing stand-alone svg output (as opposed to creating the graph with javascript, which is the case in ASCIIsvg and JSXGraph) is that it's quicker, because there is less for the browser to download (no javascript), and there is no javascript processing required during page load to produce the graph. It's also quicker on mobile devices, because the server does the processing work, rather than the device and its browser.

Download

Requirements You need a PHP-based server (on your local machine, or a real server).

You can download a working copy of svgPHPGrapher here:

Download svgPHPGrapher

svgPHPGrapher Example Chapters

Apart from the examples below, you can see example graph output from svgPHPGrapher in these IntMath chapters:

On this page...

Board Requirements

Somewhere at the top of your page, add the following (this grabs the latest version of svgPHPGrapher.php).

<?php
include('../includes/getLatestsvgPHPGrapher.php');
$brdNum=0; // For svg id's
?>

Drawing a Board with Axes

The board, grid and axes above were produced using the following code:

<?php
// Create the board
$boardID = "svgphp-$brdNum"; $brdNum++;
$width = 310; $height = 200;
$xMin=-4; $xMax=6; $yMin=-3; $yMax=''; 
$padding = 15;
list($svgArr, $htmlArr, $brdParams) = initBoard($boardID, $width, $height, $xMin, $xMax, $yMin, $yMax, $axesType, $res1, $res2, $res3, $padding);

// Create the axes
$xAxisTicks = 1;	$yAxisTicks = 2;
$axisNumbering = "labels";
$verticalGridGap = 2;	$horizontalGridGap = 2;
$gridAttrs = 'stroke:#777; stroke-width:0.5';
$axesAttrs = 'stroke:#444; stroke-width:1';
$xAxisVbl = "x";	$yAxisVbl = "y";
$arrows = 1;
$svgArr[] = axes($xAxisTicks,$yAxisTicks, $axisNumbering, $verticalGridGap,$horizontalGridGap, $xAxisVbl,$yAxisVbl, $gridAttrs,$axesAttrs, $arrows, $brdParams);

// Echo out the results
doOutput($svgArr,$htmlArr);
?>

Explanation

Each board on the page automatically gets a sequential ID:

$boardID = "svgphp-$brdNum"; $brdNum++;

This is your graph size (in pixels):

$width = 310; $height = 200;

This is the range of possible values in the horizontal and vertical axes.

$xMin=-4; $xMax=6; $yMin=-3;  $yMax=''; 

Leaving $yMax as an empty string means the board will have equal-scaled axes.

This next one gives some space around the edge so you can see all of the axes numbers (a good default size is 15):

$padding = 15;

This is the first function call where you initialize the board. Nothing to do here, as you have defined all that's needed above.

list($svgArr, $htmlArr, $brdParams) = initBoard($boardID, $width, $height, $xMin, $xMax, $yMin, $yMax, $axesType, $res1, $res2, $res3, $padding);

Next is the function that echoes out the final output:

doOutput($svgArr,$htmlArr);

Adding the axes

The gaps between ticks on your axes:

$xAxisTicks = 1;	$yAxisTicks = 2;

If you want the numbers on your axes to show, put "labels" if so, or an empty string if not: "":

$axisNumbering = "labels";

The gaps between grids (x-direction, then y-direction):

$verticalGridGap = 2;	$horizontalGridGap = 2;

The $gridAttrs and $axesAttrs have defaults (see the top of the svgPHPGrapher function), but you can override colors and thickness if you want.

Here's where you state what axis variables you want. Leaving them blank (like $xAxisVbl = '') will mean no axis label will appear.

$xAxisVbl = "x";	$yAxisVbl = "y";

This is if you want arrows on your axes (value 1) or not (value 0).

$arrows = 1;

Now for the axes function. (If you put $yAxisTicks = '' it means no y-axis.)

$svgArr[] = axes($xAxisTicks,$yAxisTicks, $axisNumbering, $verticalGridGap,$horizontalGridGap, $xAxisVbl,$yAxisVbl, $gridAttrs,$axesAttrs, $arrows, $brdParams);

This closes the svg tag and prints out the resulting graph to the screen:

$svgArr[] = '</svg>';
doOutput($svgArr,$htmlArr);

Multiples of `pi` on axes

The above axes use the following call to the axes function:

$svgArr[] = axes(pi(),1,"pi,numbers",pi(),2, $xAxisVbl, $yAxisVbl, $gridAttrs, $axesAttrs, $arrows, $brdParams);

Plotting Mathematical Functions

The above polynomial was plotted as follows.

The function is declared like this:

$fun = "x^4 + 0.4x^3 - 6.49x^2 + 7.244x - 2.112";

You indicate the start and end x-values for the curve as follows (leaving $xStart blank means the curve will start from the far left side of the graph, and leaving $xEnd blank means the domain extends all the way to the right):

$xStart = 0.5;	$xEnd = '';

You can set the number of points for the curve (lower numbers means a "chunkier" graph, higher means a smoother graph but larger file size):

$numPts = 80;

These are the attributes for the curve:

$attrs = 'stroke:#165a71; stroke-width:1.5; fill:none';

The syntax for the plot function is:

$svgArr[] = plot($fun, $xStart,$xEnd, $numPts, $attrs,$brdParams);

The following syntax produces the dots at the x-axis intersections:

$attrs = 'stroke:none; fill:#f0f';
$svgArr[] = dot([1.2,0], 3, $attrs,$brdParams);
$svgArr[] = dot([0.5,0], 3, $attrs,$brdParams);
$svgArr[] = dot([1.1,0], 3, $attrs,$brdParams);

The parameters for the dot function are:

$center, $radius, $attrs, $brdParams

NOTE: Possible independent variables are `x` and `t`.

Vertical Graph Cases

Here's a common problem when constructing graphs using a computer-based grapher. If the graph becomes very steep, the only way to ensure all of it is included is to have a very large number of sample points. But this is not a good idea as:

  • It results in a large file size
  • It's not necessary to have a large number of points for the graph in parts where it is not so steep.

Let's illustrate the problem with an example. Here I'm trying to draw the graph of `x=y^2+2`. This is a relation (not a function) and I need to draw it in 2 halves:

  1. The top half, `y=sqrt(x-2)`; and
  2. The bottom half, `y=-sqrt(x-2)`.

As you can see, there is a gap near `x=2` where the top and bottom half should meet.

Here's the syntax I've used:

$svgArr[] = plot("sqrt(x-2)",'','',60, $attrs,$brdParams);
$svgArr[] = plot("-sqrt(x-2)",'','',60, $attrs,$brdParams);

In svgPHPGrapher, there is a built-in attempt to solve this. When the graph is steep, the number of points sampled increases by a factor of 10. But even with this enhancement, you can see there still is a gap.

If I increase the number of points for each of the arms of the graph from 60 to 400 (giving a total of 800 points for the 2 arms plus the extra ones generated near the gap, I still get a gap.

Here's one way to solve this issue. We graph the inverse instead.

Graph the Inverse

It's possible in svgPHPGrapher to plot an inverse. In this example, the inverse of `y=+-sqrt(x-2)` is `x=y^2+2`. Here's what the graph looks like.

This is the syntax used this time:

$svgArr[] = plot("y^2+2",'','',40, $attrs,$brdParams);

Notice the use of the `y` variable. Internally, the grapher takes `y`-values from the minimum `y`-value to the maximum `y`-value. You'll notice that with only 40 sample points, the curve is smooth and connected, and there is no trouble when the graph is vertical at `x=2`.

Graphs with Discontinuities

Similar problems occur when we try to draw periodic graphs with vertical asymptotes, for example, `y=tan x`.

You can see that the grapher has connected the top of each positive arm of the curve to the bottom of the negative (supposedly infinite) negative next arm. You can also see the graph doesn't go down all the way to the minimum negative value.

Using the inverse function, we can get a better looking tan curve, with no joins and a small number of points.

What I've done here is to graph the inverse tan curve using the `y` variable: `"atan"(y)` (which means `arctan (y)`). I then replicate that single curve as many times as needed to cover the board.

Here's the syntax for the loop:

for($k=-pi(); $k<=2*pi(); $k+=pi()) {
$attrs = 'stroke:#165a71; stroke-width:1.5;fill:none';
$svgArr[] = plot("atan(y)+$k", $yMin,$yMax, 60, $attrs,$brdParams);

$attrs = 'stroke:#165a71; stroke-width:1; stroke-dasharray:5,5';
$svgArr[] = line([$k+pi()/2, $yMin], [$k+pi()/2, $yMax], $attrs,$brdParams);
}

The first 2 lines in the loop are responsible for repeating the single `"atan"(y)` curve, and the last 2 lines produce the dashed vertical asymptotes.

Here are the cuves `y="atan"x` (in magenta) and `x="atan"y` (in green) drawn on the one set of axes. Since they are inverses, they are reflections in the line `y=x`, which is also shown.

Here's another example where I've plotted a curve `y=sin x` and its inverse `x= sin y` on the one set of axes, and shown the reflection line `y=x`.

Here's another place where I needed to use the inverse function idea.

The 2 arms of the green hyperbola would have proved difficult using ordinary `f(x)` approach. This is the syntax used:

$attrs = 'stroke:#165a71; stroke-width:1.5; fill:none';
$svgArr[] = plot("sqrt((y^2 + 4)/3)",$yMin,$yMax,40, $attrs,$brdParams);
$svgArr[] = plot("-sqrt((y^2 + 4)/3)",$yMin,$yMax,40, $attrs,$brdParams);

Finally, some graphs have both a steep part and a gently rising part. For example, the graph of `y=log_10 x`. Here I've drawn it in 2 parts.

The green part (quite shallow, above `y=-1`) is:

$attrs = 'stroke:#165a71; stroke-width:1.5; fill:none';
$svgArr[] = plot("(ln(x))/(ln(10))",0.1,'',40, $attrs,$brdParams);

The magenta part (almost vertical to start with, below `y=-1`) is:

$attrs = 'stroke:#f0f; stroke-width:1.5; fill:none';
$svgArr[] = plot("10^y",$yMin,-1,40, $attrs,$brdParams);

Line through 2 points

A line extends across the whole board. Here is the syntax for the above line, which passes through `(-2,-1)` and `(2,2)`:

$attrs = 'stroke:#165a71; stroke-width:1.5';
$svgArr[] = line([-2,-1], [2,2], $attrs,$brdParams);

Segments

Simple segment

Here is the syntax for the segment joining `(-3,2)` and `(3,-3)`:

$p = [-3,2];  $q = [3,-3];  
$attrs = 'stroke:#165a71; stroke-width:2;';
$svgArr[] = segment($p, $q,  $attrs,$brdParams);

Segments can be embellished with various markers (dots or arrows).

Segment-Arrow

This one is useful for drawing vectors. You can change the color and size of the arrow head.

Here is the syntax used for the first segment, pointing down:

$p = [1,-1];  $q = [1,-2];  
$segAttrs = 'stroke:#f0f; stroke-width:2;';
$arrAttrs = 'triWidth:8; triHeight:12; stroke:#165a71; stroke-width:1; fill:#3df;';
$svgArr[] = segArrow($p, $q,  $segAttrs, $arrAttrs,$brdParams);

Here is the second grey segment, with green arrow head:

$p = [2,-2.5];  $q = [5,-1];
$segAttrs = 'stroke:#888; stroke-width:2;'; 
$arrAttrs = 'triWidth:15; triHeight:20; stroke:#165a71; stroke-width:1; fill:#4f4;';
$svgArr[] = segArrow($p, $q,  $segAttrs, $arrAttrs,$brdParams);

Dot-Segment-Dot

You can have different-size (and colored) dots on the end of your segments, as follows:

Here is the syntax used for the first "dot-segment-dot" segment. Note the 2 dots ("dot0" and "dot1" have their own attributes.)

$p = [-3,0];  $q = [4,2];  
$dot0Attrs = 'radius:5; stroke:#165a71; stroke-width:2; fill:none';
$segAttrs = 'stroke:#165a71; stroke-width:2';
$dot1Attrs = 'radius:5; stroke:#165a71; stroke-width:2; fill:none';
$svgArr[] = dotSegDot($p, $q, $dot0Attrs,$segAttrs,$dot1Attrs, $brdParams);

Here is the second multi-colored and multi-sized one:

$p = [-3,-3];  $q = [4,-1];  
$dot0Attrs = 'radius:10; stroke:#165a71; stroke-width:2; fill:#f0f; fill-opacity:0.4';
$segAttrs = 'stroke:#888; stroke-width:2;;
$dot1Attrs = 'radius:3; stroke:none; fill:#f55';
$svgArr[] = dotSegDot($p, $q, $dot0Attrs,$segAttrs,$dot1Attrs, $brdParams);

Dot-Segment-Arrow

You can have your segment starting with a dot, and ending with an arrow, as follows:

The first 2 segments (on the left) share the same attributes:

$dotAttrs = 'radius:5; stroke:#165a71; stroke-width:2; fill:none';
$segAttrs = 'stroke:#165a71; stroke-width:2;';
$arrAttrs = 'triWidth:10; triHeight:15; stroke:#165a71; stroke-width:1; fill:none';
$svgArr[] = dotSegArrow([-2,-2], [2,-3], $dotAttrs, $segAttrs, $arrAttrs,$brdParams);
$svgArr[] = dotSegArrow([-2,-1], [0,0], $dotAttrs, $segAttrs, $arrAttrs,$brdParams);

This is the segment with larger features at top right (I've toned it down by reducing the opacity):

$dotAttrs = 'radius:15; stroke:#165a71; stroke-width:2; fill:#165a71;opacity:0.5';
$segAttrs = 'stroke:#165a71; stroke-width:4;opacity:0.5';
$arrAttrs = 'triWidth:20; triHeight:20; stroke:#165a71; stroke-width:1; fill:#165a71;opacity:0.5';
$svgArr[] = dotSegArrow([5,1], [3,-1], $dotAttrs, $segAttrs, $arrAttrs,$brdParams);

This is the more subtle segment at bottom right:

$dotAttrs = 'radius:3; stroke:#165a71; stroke-width:2; fill:#165a71';
$segAttrs = 'stroke:#165a71; stroke-width:1';
$arrAttrs = 'triWidth:10; triHeight:15; stroke:#165a71; stroke-width:1; fill:#165a71';
$svgArr[] = dotSegArrow([5,-2.5], [2,-3], $dotAttrs, $segAttrs, $arrAttrs,$brdParams);

Drawing Inequalities Graphs

The above segments can be used to draw inequalities, as follows.

No y-axis, no grids, no y-axis variable name

We can "turn off" the y-axis like this:

Here is the syntax used:

$xAxisTicks = 1;	$yAxisTicks = '';
$axisNumbering = "labels";
$verticalGridGap = 0;	$horizontalGridGap = 0;
$gridAttrs = '';
$axesAttrs = 'stroke:#444; stroke-width:1';
$xAxisVbl = "x";	$yAxisVbl = "";
$arrows = 0;

The above configuration is useful for presenting inequality solutions, as follows.

Open one end, closed the other:

The above example uses the "dot-segment-dot" type, like this:

$p = [-2,0]; $q =  [4,0];
$dot0Attrs= 'radius:5; stroke:#165a71; fill:none; stroke-width:1.5';
$segAttrs = 'stroke:#165a71; stroke-width:3';
$dot1Attrs = 'radius:5; stroke:#165a71; fill:#165a71; stroke-width:1.5';
$svgArr[] = dotSegDot($p, $q, $dot0Attrs,$segAttrs,$dot1Attrs, $brdParams);

Open dot with pointing arrow:

The line segment, opening circle and final arrow in the above graph are achieved with:

$p = [2,0]; $q =  [-2,0];
$dot0Attrs= 'radius:5; stroke:#165a71; fill:none; stroke-width:1.5';
$segAttrs = 'stroke:#165a71; stroke-width:3';
$arrAttrs = 'triWidth:10; triHeight:15; stroke:#165a71; stroke-width:1; fill:#165a71';
$svgArr[] = dotSegArrow($p, $q, $dot0Attrs,$segAttrs,$arrAttrs, $brdParams);

NOTE: dots are always circles, whether the axes are equal scaled or not.

Log-log and Semi-log Graphs

For log-log and semi-logarithmic graphs, we need to call the logAxes function, instead of the ordinary axes function.

Log-log graphs

100 10 1 0.1 P T
100
101
102

Here is the syntax for the log-log graph type:

$xMin=0.1; $xMax=100; $yMin=1;  $yMax=100; 
$padding = 28;
$axesType = "loglog"; // log vertical, log horizontal
list($svgArr, $htmlArr, $brdParams) = initBoard($boardID, $width, $height, $xMin, $xMax, $yMin, $yMax, $axesType, $res1, $res2, $res3, $padding);
$gridAttrs = 'stroke:#ccc; stroke-width:1; shape-rendering:crispEdges';
$axesAttrs = 'stroke:#444; stroke-width:1; shape-rendering:crispEdges';
$xAxisVbl = "T";	$yAxisVbl = "P";
$arrows = 1;
$svgArr[] = logaxes(1,1,"labels",1,2, $xAxisVbl, $yAxisVbl, $gridAttrs, $axesAttrs, $arrows, $brdParams);
$attrs = 'stroke:#165a71; stroke-width:1.5; fill:none';
$svgArr[] = plot("40+30sin(x/5)",'',$xMax,300, $attrs,$brdParams);

Log-linear graphs

This is the syntax for the vertical logarithm and horizontal linear graph type:

Here's what you need to do for the log-linear graph type.

$xMin=-4; $xMax=10; $yMin=1;  $yMax=100; 
$padding = 20;
$axesType = "loglin"; // log vertical, linear horizontal
list($svgArr, $htmlArr, $brdParams) = initBoard($boardID, $width, $height, $xMin, $xMax, $yMin, $yMax, $axesType, $res1, $res2, $res3, $padding);

$gridAttrs = 'stroke:#ccc; stroke-width:1; shape-rendering:crispEdges';
$axesAttrs = 'type:log-linear; stroke:#444; stroke-width:1; shape-rendering:crispEdges';
$xAxisVbl = "T";	$yAxisVbl = "P";
$arrows = 1;

$svgArr[] = logaxes(1,1,"labels",1,2, $xAxisVbl, $yAxisVbl, $gridAttrs, $axesAttrs, $arrows, $brdParams);  //$dx,$dy,$labels,$gdx,$gdy, $xAxisVbl, $yAxisVbl, $attrs,$brdParams
$attrs = 'stroke:#165a71; stroke-width:1.5; fill:none';
$svgArr[] = plot("40+30sin(x)",'',$xMax,300, $attrs,$brdParams);

Polar Graphs

For polar graphs, we need to call the polarAxes function, instead of the ordinary axes function.

Here is the syntax:

$rGap = 1;
$degRad = "deg";
$qGap = 15;
$angLabelGap = 30;
$gridAttrs = 'stroke:#ccc; stroke-width:1; fill:none; shape-rendering:crispEdges';
$axesAttrs = 'stroke:#ccc; stroke-width:1; fill:none;';
$svgArr[] = polarAxes($rGap,"labels",$degRad,$qGap,$angLabelGap,  $gridAttrs, $axesAttrs, $brdParams);

Here's the graph:

The gap between each angular axis (circle) in the grid (and the gap between numbers along the axis):

$rGap = 1;

Whether you want radians or degrees for your angle measure:

$degRad = "deg";

The gap between the radial axes (the lines) as an angle (in this case 15°):

$qGap = 15;

The gap between angle labels (the numbers around the outer circle, in this case 30°):

$angLabelGap = 30;

The next part is the standard grid and axes attributes:

$gridAttrs = 'stroke:#ccc; stroke-width:1; fill:none; shape-rendering:crispEdges';
$axesAttrs = 'stroke:#ccc; stroke-width:1; fill:none;';
$svgArr[] = polarAxes($rGap,"labels",$degRad,$qGap,$angLabelGap,  $gridAttrs, $axesAttrs, $brdParams);

Now for the polar curve. Notice we use polarPlot function.

We use "q" for the angle variable (q is commonly used for θ).

$attrs = 'stroke:#165a71; stroke-width:1.5; fill:none';
$svgArr[] = polarplot("3sin(2q)",0,2*pi(),100, $attrs,$brdParams);

The function paramaters are:

$fun,$x_min,$x_max,$points, $attrs,$brdParams

Parametric Curve

Here's a curve traced out by functions in the form `x = x(t)`, and `y=y(t)`. We need to empty the $plist array first, since it may contain values from an example above.

Here's the syntax used:

$plist = [];
$attrs = 'stroke:#165a71; stroke-width:1.5; fill:none';
for ($t = -7*pi()/2; $t < 7*pi()/2+0.08; $t += 0.08) {
	$plist[] = [$t*cos($t), $t*sin($t)];
}
$svgArr[] = path($plist,'','', $attrs,$brdParams);

Cardinal Splines

A cardinal spline is a smooth curve that passes through a set of points. This is different from a regression curve which is an "average" curve, and doesn't necessarily pass through any of the points.

You need to provide a list of points and some attributes, then generate the $plist array. Finally, provide the $plist array to the path function.

The $numSegs changes how smooth the curve is (its the number of points between each supplied point).

Here's the syntax:

$ptsArr = [ [2,2], [5,-2], [1,-1], [-3,-2], [-3,1] ];
$tension = 0.7;
$isClosed = true;
$numSegs = 20;
$plist = getCurvePoints($ptsArr, $tension, $isClosed, $numSegs);
$attrs = 'stroke:#165a71; stroke-width:1.5; fill:#165a71;fill-opacity:0.15';
$svgArr[] = path($plist,'','', $attrs,$brdParams);
$attrs = 'stroke:none; fill:#165a71';
foreach($ptsArr as $pt) {
  $svgArr[] = dot($pt,3,$attrs, $brdParams);
}

Geometric Shapes

Ellipses and Circles

Here we've drawn both an ellipse and a circle, using this syntax:

$attrs = 'stroke:#165a71; stroke-width:3; fill:#5a9fb5; opacity:0.5';
$svgArr[] = ellipse([-2,-3],1,4,"myEllipse", $attrs, $brdParams); //ellipse($center,$rx,$ry,$id, $attrs, $brdParams)


$attrs = 'stroke:#165a71; stroke-width:3; fill:#ff8; opacity:0.5';
$svgArr[] = circle([3,3],2,"myCirc", $attrs, $brdParams);

However, the circle isn't right because we haven't used equal-scaled axes. Second try:

This time, I set $yMax=''; so the axes would have equal scaling.

Arcs

For an arc, we need to set the beginning and end points, then the radius of the arc. We can also have our arc filled, or not.

This is the first one, which is filled:

$attrs = 'stroke:#165a71; stroke-width:3; fill:#5a9fb5; opacity:0.5';
$svgArr[] = arc([-2,2],[3,-2],4, $attrs, $brdParams);

Now for the second one, without fill:

$attrs = 'stroke:#165a71; stroke-width:3; fill:none; opacity:0.5';
$svgArr[] = arc([2,0],[0,2],2, $attrs, $brdParams);

Angle arcs

These are simply an arc with an arrow at the end, using the angleArcArrow function.

Function parameters:

angleArcArrow($p, $radius, $startAngle, $endAngle, $arcAttrs, $arrAttrs,$brdParams); {  // pixels and degrees

This is the third angle arc shown (the one in the second quadrant:

$p = [0,0];  $q = [-1,2]; 
$svgArr[] = segArrow($p, $q,  $segAttrs, $arrAttrs,$brdParams);
$svgArr[] = angleArcArrow([0,0], 60, 0, 180 + 180*atan( $q[1]/$q[0])/PI(), $attrs, $brdParams);

Rectangles and Squares

Here's the syntax for the rectangle. The points are the bottom left, then top right of the rectangle.

$attrs = 'stroke:#165a71; stroke-width:3; fill:#5a9fb5; fill-opacity:0.1';
$svgArr[] = rect([-3.5,-1],[-1,3], $attrs,$brdParams);

Now for the square. We specify the bottom-left of the square, and the next parameter is its width.

$attrs = 'stroke:#165a71; fill:#ff8; stroke-width:1.5; rx:8; opacity:0.7';
$svgArr[] = square([1, -2], 2.5, $attrs,$brdParams);

Rounded corners are achieved using rx:8

Triangles

For triangles, we specify the 3 vertices. Here's the first one:

$p = [-4,-2]; $q =  [-1,2]; $r = [-2,-2];
$attrs = 'stroke:#165a71; stroke-width:3; fill:#5a9fb5; fill-opacity:0.1';
$svgArr[] = triangle($p,$q,$r, $attrs,$brdParams);

Now for the second one:


$p = [-1,0]; $q =  [4,0]; $r = [2,1];
$attrs = 'stroke:#165a71; fill:#ff8; stroke-width:1.5; opacity:0.7';
$svgArr[] = triangle($p,$q,$r, $attrs,$brdParams);

Current grapher version = svgPHPGrapher2018-12-18.php

Tips, tricks, lessons, and tutoring to help reduce test anxiety and move to the top of the class.