Skip to content

Commit

Permalink
pegboard for graph (Doenet#1968)
Browse files Browse the repository at this point in the history
  • Loading branch information
dqnykamp authored Mar 6, 2023
1 parent d06e9c2 commit f84503d
Show file tree
Hide file tree
Showing 4 changed files with 297 additions and 0 deletions.
35 changes: 35 additions & 0 deletions cypress/e2e/DoenetML/tagSpecific/graph.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -1942,6 +1942,41 @@ describe('Graph Tag Tests', function () {
cy.get('#\\/pdc5d').should('have.text', '-45.03233, 8.28572, -5.58234, 7.8371')


});

it('pegboard', () => {
cy.window().then(async (win) => {
win.postMessage({
doenetML: `
<text>a</text>
<graph>
<pegboard />
</graph>
<graph>
<pegboard dx="3" dy="2" xoffset="1" yoffset="-1" />
</graph>
`}, "*");
});

cy.get('#\\/_text1').should('have.text', 'a') //wait for page to load

// not sure what to test as don't know how to check renderer...
cy.window().then(async (win) => {
let stateVariables = await win.returnAllStateVariables1();
expect(stateVariables["/_pegboard1"].stateValues.dx).eq(1);
expect(stateVariables["/_pegboard1"].stateValues.dy).eq(1);
expect(stateVariables["/_pegboard1"].stateValues.xoffset).eq(0);
expect(stateVariables["/_pegboard1"].stateValues.yoffset).eq(0);
expect(stateVariables["/_pegboard2"].stateValues.dx).eq(3);
expect(stateVariables["/_pegboard2"].stateValues.dy).eq(2);
expect(stateVariables["/_pegboard2"].stateValues.xoffset).eq(1);
expect(stateVariables["/_pegboard2"].stateValues.yoffset).eq(-1);
})



});

});
2 changes: 2 additions & 0 deletions src/Core/ComponentTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ import Map from './components/Map';
import Sources from './components/Sources';
import Slider from './components/Slider';
import Markers from './components/Markers';
import Pegboard from './components/Pegboard';
import Constraints from './components/Constraints';
import ConstrainToGrid from './components/ConstrainToGrid';
import ConstrainToGraph from './components/ConstrainToGraph';
Expand Down Expand Up @@ -258,6 +259,7 @@ const componentTypeArray = [
Markers,
Panel,
Map, Sources,
Pegboard,
Constraints,
ConstrainToGrid,
ConstrainToGraph,
Expand Down
45 changes: 45 additions & 0 deletions src/Core/components/Pegboard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import GraphicalComponent from './abstract/GraphicalComponent';

export default class Pegboard extends GraphicalComponent {
static componentType = "pegboard";

static createAttributesObject() {
let attributes = super.createAttributesObject();

attributes.dx = {
createComponentOfType: "number",
createStateVariable: "dx",
defaultValue: 1,
public: true,
forRenderer: true
};

attributes.dy = {
createComponentOfType: "number",
createStateVariable: "dy",
defaultValue: 1,
public: true,
forRenderer: true
};

attributes.xoffset = {
createComponentOfType: "number",
createStateVariable: "xoffset",
defaultValue: 0,
public: true,
forRenderer: true
};

attributes.yoffset = {
createComponentOfType: "number",
createStateVariable: "yoffset",
defaultValue: 0,
public: true,
forRenderer: true
};

return attributes;

}

}
215 changes: 215 additions & 0 deletions src/Viewer/renderers/pegboard.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
import React, { useContext, useEffect, useState, useRef } from 'react';
import useDoenetRender from './useDoenetRenderer';
import { BoardContext } from './graph';

export default React.memo(function Pegboard(props) {
let { name, id, SVs, actions, sourceOfUpdate, callAction } = useDoenetRender(props);

Pegboard.ignoreActionsWithoutCore = true;

const board = useContext(BoardContext);

let pegboardJXG = useRef(null);

let previousBounds = useRef(null);

let dx = useRef(null);
let dy = useRef(null);
let xoffset = useRef(null);
let yoffset = useRef(null);

dx.current = SVs.dx;
dy.current = SVs.dy;
xoffset.current = SVs.xoffset;
yoffset.current = SVs.yoffset;

let jsxPointAttributes = useRef({
visible: !SVs.hidden,
fixed: true,
withlabel: false,
layer: 10 * SVs.layer,
fillColor: "var(--canvastext)",
strokeColor: "var(--canvastext)",
size: 0.1,
face: "circle",
highlight: false,
showinfobox: false,
});

jsxPointAttributes.current.visible = !SVs.hidden;
jsxPointAttributes.current.layer = 10 * SVs.layer;

useEffect(() => {
//On unmount
return () => {
deletePegboardJXG();
}
}, [])



function createPegboardJXG() {

let [xmin, ymax, xmax, ymin] = board.getBoundingBox();

let xind1 = (xmin - xoffset.current) / dx.current;
let xind2 = (xmax - xoffset.current) / dx.current;
let yind1 = (ymin - yoffset.current) / dy.current;
let yind2 = (ymax - yoffset.current) / dy.current;

let minXind = Math.floor(Math.min(xind1, xind2));
let maxXind = Math.ceil(Math.max(xind1, xind2));
let minYind = Math.floor(Math.min(yind1, yind2));
let maxYind = Math.ceil(Math.max(yind1, yind2));

previousBounds.current = [minXind, maxXind, minYind, maxYind];

if (Number.isFinite(minXind) && Number.isFinite(maxXind) && Number.isFinite(minYind) && Number.isFinite(maxYind)) {

let pegs = [];

for (let yind = minYind; yind <= maxYind; yind++) {
let y = yind * SVs.dy + SVs.yoffset;
let row = [];
for (let xind = minXind; xind <= maxXind; xind++) {
row.push(board.create('point', [xind * SVs.dx + SVs.xoffset, y], jsxPointAttributes.current));
}
pegs.push(row);
}

pegboardJXG.current = pegs;

}


board.on('boundingbox', () => {

let [xmin, ymax, xmax, ymin] = board.getBoundingBox();

let xind1 = (xmin - xoffset.current) / dx.current;
let xind2 = (xmax - xoffset.current) / dx.current;
let yind1 = (ymin - yoffset.current) / dy.current;
let yind2 = (ymax - yoffset.current) / dy.current;

let minXind = Math.floor(Math.min(xind1, xind2));
let maxXind = Math.ceil(Math.max(xind1, xind2));
let minYind = Math.floor(Math.min(yind1, yind2));
let maxYind = Math.ceil(Math.max(yind1, yind2));

let [prevXmin, prevXmax, prevYmin, prevYmax] = previousBounds.current;

if (minXind !== prevXmin || maxXind !== prevXmax || minYind !== prevYmin || maxYind !== prevYmax) {

recalculatePegboard(minXind, maxXind, minYind, maxYind)
}

})
}

function deletePegboardJXG() {
if (pegboardJXG.current !== null) {
for (let row of pegboardJXG.current) {
for (let point of row) {
board.removeObject(point)
}
}
}

pegboardJXG.current = null;

}


function recalculatePegboard(minXind, maxXind, minYind, maxYind) {

if (pegboardJXG.current === null) {
return createPegboardJXG();
}

if (!Number.isFinite(minXind) || !Number.isFinite(maxXind) || !Number.isFinite(minYind) || !Number.isFinite(maxYind)) {
return deletePegboardJXG();
}


let [prevXmin, prevXmax, prevYmin, prevYmax] = previousBounds.current;

let nRows = maxYind - minYind + 1;
let prevNrows = prevYmax - prevYmin + 1;
let nCols = maxXind - minXind + 1;
let prevNcols = prevXmax - prevXmin + 1;

for (let i = 0; i < Math.min(nRows, prevNrows); i++) {
let row = pegboardJXG.current[i];
let y = (i + minYind) * dy.current + yoffset.current;

for (let j = 0; j < Math.min(nCols, prevNcols); j++) {

let x = (j + minXind) * dx.current + xoffset.current;

row[j].coords.setCoordinates(JXG.COORDS_BY_USER, [x, y]);

row[j].needsUpdate = true;
row[j].update();
}
if (prevNcols > nCols) {
for (let j = nCols; j < prevNcols; j++) {
let point = row.pop();
board.removeObject(point)
}
} else if (prevNcols < nCols) {
for (let j = prevNcols; j < nCols; j++) {
let x = (j + minXind) * dx.current + xoffset.current;
row.push(board.create('point', [x, y], jsxPointAttributes.current));
}
}
}

if (prevNrows > nRows) {
for (let i = nRows; i < prevNrows; i++) {
let row = pegboardJXG.current.pop();
for (let j = 0; j < prevNcols; j++) {
let point = row.pop();
board.removeObject(point)
}
}
} else if (prevNrows < nRows) {
for (let i = prevNrows; i < nRows; i++) {
let row = [];
let y = (i + minYind) * dy.current + yoffset.current;
for (let j = 0; j < nCols; j++) {
let x = (j + minXind) * dx.current + xoffset.current;
row.push(board.create('point', [x, y], jsxPointAttributes.current));
}
pegboardJXG.current.push(row);
}
}

previousBounds.current = [minXind, maxXind, minYind, maxYind];

board.updateRenderer();
}

if (board) {
if (pegboardJXG.current === null) {
createPegboardJXG();
} else {
let [xmin, ymax, xmax, ymin] = board.getBoundingBox();

let xind1 = (xmin - xoffset.current) / dx.current;
let xind2 = (xmax - xoffset.current) / dx.current;
let yind1 = (ymin - yoffset.current) / dy.current;
let yind2 = (ymax - yoffset.current) / dy.current;

let minXind = Math.floor(Math.min(xind1, xind2));
let maxXind = Math.ceil(Math.max(xind1, xind2));
let minYind = Math.floor(Math.min(yind1, yind2));
let maxYind = Math.ceil(Math.max(yind1, yind2));

recalculatePegboard(minXind, maxXind, minYind, maxYind)

}
}

return null;

})

0 comments on commit f84503d

Please sign in to comment.