OiO.lk Blog javascript Creating graphs in slope intercept form
javascript

Creating graphs in slope intercept form


I want to create a worksheet generator that can export to pdf. I want the graphs to be created in slope intercept form with at least two visible coordinates on the graph that align with x and y axis values that are labeled and a y-intercept that goes through a visibly labeled whole number. The intervals should change for each graph. currently there are 20 gridlines on each axis, i want to be able to create smaller graphs with 10 as well. The user should be able to select 10 or 20. I attached an example image.

import React, { useState } from "react";
import { VictoryChart, VictoryLine, VictoryAxis } from "victory";
import jsPDF from "jspdf";
import html2canvas from "html2canvas";

function WorksheetGenerator() {
  const [numQuestions, setNumQuestions] = useState(5);
  const [displayMode, setDisplayMode] = useState("both");
  const [worksheetOutput, setWorksheetOutput] = useState([]);
  const [selectedItems, setSelectedItems] = useState([]);

  const generateWorksheet = () => {
    const output = [];

    for (let i = 0; i < numQuestions; i++) {
      const slope = randomSlope(); // Allows negative slope
      const intervalX = randomInterval(); // Interval for x-axis
      const intervalY = randomInterval(); // Interval for y-axis
      const intercept = randomVisibleIntercept(intervalY);

      if (
        displayMode === "tables" ||
        displayMode === "both" ||
        displayMode === "matching"
      ) {
        const table = generateTable(slope, intercept, intervalX);
        output.push(
          <div key={`table-${i}`} id={`table-${i}`}>
            {table}
            <label>
              <input
                type="checkbox"
                onChange={(e) => handleCheckboxChange(e, `table-${i}`)}
              />{" "}
              Include this table in worksheet
            </label>
          </div>
        );
      }

      if (
        displayMode === "graphs" ||
        displayMode === "both" ||
        displayMode === "matching"
      ) {
        const graph = generateGraph(slope, intercept, intervalX, intervalY);
        output.push(
          <div key={`graph-${i}`} id={`graph-${i}`}>
            {graph}
            <label>
              <input
                type="checkbox"
                onChange={(e) => handleCheckboxChange(e, `graph-${i}`)}
              />{" "}
              Include this graph in worksheet
            </label>
          </div>
        );
      }
    }

    setWorksheetOutput(output);
  };

  const handleCheckboxChange = (event, id) => {
    if (event.target.checked) {
      setSelectedItems((prevItems) => [...prevItems, id]);
    } else {
      setSelectedItems((prevItems) => prevItems.filter((item) => item !== id));
    }
  };

  // Updated function to allow for negative slopes
  const randomSlope = () => {
    return Math.random() > 0.5
      ? Math.floor(Math.random() * 5) + 1 // positive slope
      : -1 * (Math.floor(Math.random() * 5) + 1); // negative slope
  };

  // Function to select an intercept that aligns with the tick marks on the y-axis
  const randomVisibleIntercept = (intervalY) => {
    const visibleIntercepts = [];
    const range = 10 * intervalY;
    for (let i = -range; i <= range; i += intervalY) {
      visibleIntercepts.push(i);
    }

    const randomIndex = Math.floor(Math.random() * visibleIntercepts.length);
    return visibleIntercepts[randomIndex]; // Return a visible intercept
  };

  const randomInterval = () => {
    const intervals = [1, 2, 3, 4, 5, 10];
    return intervals[Math.floor(Math.random() * intervals.length)];
  };

  const generateTable = (slope, intercept, intervalX) => {
    const rows = [];
    const usedXValues = new Set();

    while (usedXValues.size < 5) {
      const x = Math.floor(Math.random() * 10) - 5;
      if (!usedXValues.has(x) && x !== 0) {
        usedXValues.add(x);
      }
    }

    [...usedXValues].forEach((x) => {
      const y =
        typeof slope === "string"
          ? eval(slope) * x + intercept
          : slope * x + intercept;
      rows.push(
        <tr key={x}>
          <td style={{ border: "1px solid black", padding: "5px" }}>{x}</td>
          <td style={{ border: "1px solid black", padding: "5px" }}>{y}</td>
        </tr>
      );
    });

    return (
      <table style={{ width: "100%", borderCollapse: "collapse" }}>
        <thead>
          <tr>
            <th style={{ border: "1px solid black", padding: "5px" }}>x</th>
            <th style={{ border: "1px solid black", padding: "5px" }}>y</th>
          </tr>
        </thead>
        <tbody>{rows}</tbody>
      </table>
    );
  };

  const generateGraph = (slope, intercept, intervalX, intervalY) => {
    const xValues = [];
    const yValues = [];

    for (let x = -100; x <= 100; x += intervalX) {
      xValues.push(x);
      yValues.push(
        typeof slope === "string"
          ? eval(slope) * x + intercept
          : slope * x + intercept
      );
    }

    // Calculate additional points based on the slope and intercept
    const additionalPoints = [];
    for (let i = 1; i <= 2; i++) {
      const newX = i * intervalX;
      const newY = slope * newX + intercept;
      if (Math.abs(newY) <= 10 * intervalY) {
        // Ensure it falls within the domain
        additionalPoints.push({ x: newX, y: newY });
      }
    }

    const data = xValues
      .map((x, index) => ({
        x: x,
        y: yValues[index],
      }))
      .concat(additionalPoints);

    const xDomain = { x: [-10 * intervalX, 10 * intervalX] };
    const yDomain = { y: [-10 * intervalY, 10 * intervalY] };

    const xTicks = getTicks(xDomain.x, 20, intervalX);
    const yTicks = getTicks(yDomain.y, 20, intervalY);

    return (
      <VictoryChart
        domain={{ x: xDomain.x, y: yDomain.y }}
        height={300}
        width={300}
      >
        <VictoryLine
          data={data}
          style={{
            data: { stroke: "black", strokeWidth: 1.25 },
          }}
        />
        <VictoryAxis
          tickValues={xTicks}
          style={{
            tickLabels: { fontSize: 5, padding: 5 },
            grid: { stroke: "lightgray" },
            axis: { stroke: "black", strokeWidth: 1 },
          }}
        />
        <VictoryAxis
          dependentAxis
          tickValues={yTicks}
          style={{
            tickLabels: { fontSize: 5, padding: 5 },
            grid: { stroke: "lightgray" },
            axis: { stroke: "black", strokeWidth: 1 },
          }}
        />
      </VictoryChart>
    );
  };

  const getTicks = (domain, numTicks, interval) => {
    const ticks = [];
    for (let i = -10; i <= 10; i++) {
      ticks.push(i * interval);
    }
    return ticks;
  };

  const generatePDF = async () => {
    const doc = new jsPDF();
    let yOffset = 10;
    let xOffset = 10;
    const columnWidth = 90;
    const rowHeight = 100;
    const itemsPerRow = 2;

    for (const [index, elementId] of selectedItems.entries()) {
      const element = document.getElementById(elementId);
      if (element) {
        const canvas = await html2canvas(element, { useCORS: true });
        const imgData = canvas.toDataURL("image/png");

        doc.addImage(imgData, "PNG", xOffset, yOffset, columnWidth, rowHeight);

        if ((index + 1) % itemsPerRow === 0) {
          yOffset += rowHeight;
          xOffset = 10;
        } else {
          xOffset += columnWidth + 10;
        }
      }
    }

    doc.save("worksheet.pdf");
  };

  return (
    <div>
      <form>
        <label>
          Number of questions:
          <input
            type="number"
            min="1"
            value={numQuestions}
            onChange={(e) => setNumQuestions(parseInt(e.target.value))}
          />
        </label>
        <br />

        <label>
          Display:
          <select
            value={displayMode}
            onChange={(e) => setDisplayMode(e.target.value)}
          >
            <option value="both">Both Tables and Graphs</option>
            <option value="tables">Tables Only</option>
            <option value="graphs">Graphs Only</option>
            <option value="matching">Matching Tables and Graphs</option>
          </select>
        </label>
        <br />
        <button type="button" onClick={generateWorksheet}>
          Generate Worksheet
        </button>
        <button type="button" onClick={generatePDF}>
          Download PDF
        </button>
      </form>
      <div>{worksheetOutput}</div>
    </div>
  );
}

export default WorksheetGenerator;```    
   



You need to sign in to view this answers

Exit mobile version