I am currently making a graphing calculator in C++. I wanted to make multiple versions of said graphing calculator. This first attempt uses SVG and JS to create graphs.
When I create the file and use the SVG file I wish to display the point of the graph (where a user clicks). However, my method to display the coordinates is not working. I click the graph, and nothing happens.
Code:
struct Point {
double x;
double y;
};
using Function = long double (*)(long double);
double evaluate(Function func, double x) {
return static_cast<double>(func(x));
}
void plotGraph(Function func, double minX, double maxX, double minY, double maxY, double stepSize, int SVG_SIZE, const string& hexLineColor) {
// Calculate number of points to plot
int numPoints = static_cast<int>((maxX - minX) / stepSize) + 1;
// Vector to store points
vector<Point> points(numPoints);
// Evaluate function at each step and store points
for (int i = 0; i < numPoints; ++i) {
double x = minX + i * stepSize;
double y = evaluate(func, x);
// Check if y is NaN, if so, skip this point
if (!isnan(y)) {
points[i] = {x, y};
} else {
// Set x-coordinate of the skipped point to NaN so it's not drawn
points[i] = {NAN, NAN};
}
}
// Normalize points to fit in the specified range
double xRange = maxX - minX;
double yRange = maxY - minY;
for (auto& point : points) {
if (!std::isnan(point.x)) {
point.x = (point.x - minX) * SVG_SIZE / xRange;
point.y = (point.y - minY) * SVG_SIZE / yRange;
}
}
// Calculate the x-position of the y-axis
double yAxisXPos = -minX * SVG_SIZE / xRange;
if (minX <= 0 && maxX >= 0) {
yAxisXPos = -minX / (maxX - minX) * SVG_SIZE;
} else if (minX > 0) {
yAxisXPos = 0;
} else {
yAxisXPos = SVG_SIZE;
}
// Calculate the y-position of the x-axis
double xAxisYPos;
if (minY <= 0 && maxY >= 0) {
xAxisYPos = maxY / (maxY - minY) * SVG_SIZE;
} else if (minY > 0) {
xAxisYPos = SVG_SIZE;
} else {
xAxisYPos = 0;
}
// Open SVG file for writing
ofstream svgFile("graph.svg");
// SVG header
svgFile << "<svg width="" << SVG_SIZE << "" height="" << SVG_SIZE << "" xmlns="http://www.w3.org/2000/svg">" << std::endl;
// Draw x-axis
svgFile << "<line x1="" << yAxisXPos << "" y1="0" x2="" << yAxisXPos << "" y2="" << SVG_SIZE << "" style="stroke:black;stroke-width:1"/>" << std::endl;
// Draw y-axis
svgFile << "<line x1="0" y1="" << xAxisYPos << "" x2="" << SVG_SIZE << "" y2="" << xAxisYPos << "" style="stroke:black;stroke-width:1"/>" << std::endl;
// Plot points and lines
for (int i = 1; i < numPoints; ++i) {
// Check if both x-coordinates are not NaN
if (!isnan(points[i - 1].x) && !isnan(points[i].x)) {
// Check if both y-coordinates are not NaN
if (!isnan(points[i - 1].y) && !isnan(points[i].y)) {
// Draw line between two points
svgFile << "<line x1="" << points[i - 1].x << "" y1="" << SVG_SIZE - points[i - 1].y << "" "
<< "x2="" << points[i].x << "" y2="" << SVG_SIZE - points[i].y << "" "
<< "style="stroke:#" << hexLineColor << ";stroke-width:2"/>" << std::endl;
}
}
}
// Add event handler for clicking on points
svgFile << "<script><![CDATA[" << std::endl;
svgFile << "function showCoordinates(evt) {" << std::endl;
svgFile << " var svg = evt.target.ownerDocument;" << std::endl;
svgFile << " var point = svg.createSVGPoint();" << std::endl;
svgFile << " point.x = evt.clientX;" << std::endl;
svgFile << " point.y = evt.clientY;" << std::endl;
svgFile << " var svgPoint = point.matrixTransform(svg.getScreenCTM().inverse());" << std::endl;
svgFile << " var textElement = document.createElementNS('http://www.w3.org/2000/svg', 'text');" << std::endl;
svgFile << " textElement.setAttribute('x', svgPoint.x);" << std::endl;
svgFile << " textElement.setAttribute('y', svgPoint.y);" << std::endl;
svgFile << " textElement.setAttribute('fill', 'black');" << std::endl;
svgFile << " textElement.textContent = '(' + svgPoint.x.toFixed(2) + ', ' + svgPoint.y.toFixed(2) + ')';" << std::endl;
svgFile << " svg.appendChild(textElement);" << std::endl; // Append to the SVG root
svgFile << "}" << std::endl;
svgFile << "]]></script>" << std::endl;
// Draw a transparent rectangle to capture clicks
svgFile << "<rect width="" << SVG_SIZE << "" height="" << SVG_SIZE << "" fill="transparent" onclick="showCoordinates(evt)"/>" << std::endl;
// Close SVG tag
svgFile << "</svg>";
// Close the file
svgFile.close();
cout << "Graph created in graph.svg" << endl;
}
I have tried changing the syntax a few times and I have completely changed my approach once (caused an error, so I won’t go into detail)