Skip to content

Commit

Permalink
Add equation as a sparrow endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
bgoldowsky committed Mar 27, 2024
1 parent 19f3953 commit f2a0839
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 32 deletions.
13 changes: 9 additions & 4 deletions src/plugins/graph/adornments/movable-line/movable-line-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,12 @@ import { kMovableLineType } from "./movable-line-types";
import { IGraphModel } from "../../models/graph-model";
import { IClueObject } from "../../../../models/annotations/clue-object";

export function getAnnotationId(lineKey: string, position: string) {
return `movable_line_handle:${lineKey}:${position}`;
export function getAnnotationId(lineKey: string, type: "handle"|"equation", position?: "lower"|"upper") {
if (position) {
return `movable_line_${type}:${lineKey}:${position}`;
} else {
return `movable_line_${type}:${lineKey}`;
}
}

export const MovableLineInstance = types.model("MovableLineInstance", {
Expand Down Expand Up @@ -123,8 +127,9 @@ export const MovableLineModel = AdornmentModel
const objects: IClueObject[] = [];
if (self.isVisible) {
for (const key of self.lines.keys()) {
objects.push({ tileId, objectType: "movable-line-handle", objectId: getAnnotationId(key, "lower") });
objects.push({ tileId, objectType: "movable-line-handle", objectId: getAnnotationId(key, "upper") });
objects.push({ tileId, objectType: "movable-line-handle", objectId: getAnnotationId(key, "handle", "lower") });
objects.push({ tileId, objectType: "movable-line-handle", objectId: getAnnotationId(key, "handle", "upper") });
objects.push({ tileId, objectType: "movable-line-equation", objectId: getAnnotationId(key, "equation") });
}
}
return objects;
Expand Down
57 changes: 29 additions & 28 deletions src/plugins/graph/adornments/movable-line/movable-line.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, {useCallback, useContext, useEffect, useRef, useState} from "react";
import {autorun} from "mobx";
import { observer } from "mobx-react-lite";
import {drag, select} from "d3";
import { drag, select, Selection} from "d3";
import {useAxisLayoutContext} from "../../imports/components/axis/models/axis-layout-context";
import {ScaleNumericBaseType} from "../../imports/components/axis/axis-types";
import {INumericAxisModel} from "../../imports/components/axis/models/axis-model";
Expand Down Expand Up @@ -84,6 +84,14 @@ export const MovableLine = observer(function MovableLine(props: IProps) {
}
}, [kHandle1Loc, kHandle2Loc]);

const positionEquation = useCallback((elt: Selection<HTMLElement, unknown, HTMLElement, any>, point: Point) => {
elt.style('left', `${point.x}px`)
.style('top', `${point.y}px`);
if (model.isVisible) {
annotationLocationSetter?.set(getAnnotationId(instanceKey, "equation"), point);
}
}, [annotationLocationSetter, instanceKey, model]);

// Refresh the line
useEffect(function refresh() {
const disposer = autorun(() => {
Expand Down Expand Up @@ -134,7 +142,7 @@ export const MovableLine = observer(function MovableLine(props: IProps) {
elt
.attr('cx', x)
.attr('cy', y);
const annotationId = getAnnotationId(instanceKey, index===1 ? "lower" : "upper");
const annotationId = getAnnotationId(instanceKey, "handle", index===1 ? "lower" : "upper");
if (model.isVisible) {
annotationLocationSetter?.set(annotationId, { x, y });
} else {
Expand All @@ -146,22 +154,26 @@ export const MovableLine = observer(function MovableLine(props: IProps) {
function refreshEquation() {
if (!pointsOnAxes.current) return;
const
screenX = xScale((pointsOnAxes.current.pt1.x + pointsOnAxes.current.pt2.x) / 2) / xSubAxesCount,
screenY = yScale((pointsOnAxes.current.pt1.y + pointsOnAxes.current.pt2.y) / 2) / ySubAxesCount,
attrNames = {x: xAttrName, y: yAttrName},
string = equationString(slope, intercept, attrNames),
equation = select(equationContainerSelector).select('p');
equation = select<HTMLElement,unknown>(equationContainerSelector).select<HTMLElement>('p');

select(equationContainerSelector)
.style('width', `${plotWidth}px`)
.style('height', `${plotHeight}px`);
equation.html(string);
// The equation may have been unpinned from the line if the user
// dragged it away from the line. Only move the equation if it
// is still pinned.
if (!lineModel?.currentEquationCoords) {
equation.style('left', `${screenX}px`)
.style('top', `${screenY}px`);

const fixedCoords = lineModel?.currentEquationCoords;
if (fixedCoords) {
// The equation is unpinned -- the user dragged it away from the line. Use stored position.
// It is stored in the model as a fraction of the graph height & width.
positionEquation(equation, { x: fixedCoords.x*plotWidth, y: fixedCoords.y*plotHeight });
} else {
// Pinned to line; calculate position.
const
screenX = xScale((pointsOnAxes.current.pt1.x + pointsOnAxes.current.pt2.x) / 2) / xSubAxesCount,
screenY = yScale((pointsOnAxes.current.pt1.y + pointsOnAxes.current.pt2.y) / 2) / ySubAxesCount;
positionEquation(equation, { x: screenX, y: screenY });
}
}

Expand All @@ -187,7 +199,7 @@ export const MovableLine = observer(function MovableLine(props: IProps) {
});
return () => disposer();
}, [instanceId, layout, pointsOnAxes, lineObject, plotHeight, plotWidth, xScale, yScale, model, model.lines,
xAttrName, xSubAxesCount, xAxis, yAttrName, ySubAxesCount, yAxis, xRange, yRange,
xAttrName, xSubAxesCount, xAxis, yAttrName, ySubAxesCount, yAxis, xRange, yRange, positionEquation,
equationContainerSelector, subPlotKey, instanceKey, calculateHandlePosition, annotationLocationSetter]
);

Expand Down Expand Up @@ -293,7 +305,7 @@ export const MovableLine = observer(function MovableLine(props: IProps) {

moveEquation = useCallback((event: { x: number, y: number, dx: number, dy: number }) => {
if (event.dx !== 0 || event.dy !== 0) {
const equation = select(`${equationContainerSelector} p`),
const equation = select<HTMLElement,unknown>(`${equationContainerSelector} p`),
equationNode = equation.node() as Element,
equationWidth = equationNode?.getBoundingClientRect().width || 0,
equationHeight = equationNode?.getBoundingClientRect().height || 0,
Expand All @@ -304,11 +316,10 @@ export const MovableLine = observer(function MovableLine(props: IProps) {
x = left / plotWidth,
y = top / plotHeight;

positionEquation(equation, { x: left, y: top });
model.dragEquation({x, y}, instanceKey);
equation.style('left', `${left}px`)
.style('top', `${top}px`);
}
}, [equationContainerSelector, instanceKey, model, plotWidth, plotHeight]),
}, [equationContainerSelector, plotWidth, plotHeight, positionEquation, model, instanceKey]),

endMoveEquation = useCallback(() => {
model.saveEquationCoords(instanceKey);
Expand Down Expand Up @@ -373,21 +384,11 @@ export const MovableLine = observer(function MovableLine(props: IProps) {
.style('width', `${plotWidth}px`)
.style('height', `${plotHeight}px`);

const equationP = equationDiv
.append('p')
equationDiv
.append<HTMLElement>('p')
.attr('class', 'movable-line-equation')
.attr('data-testid', `movable-line-equation-${model.classNameFromKey(subPlotKey)}`);

// If the equation is not pinned to the line, set its initial coordinates to
// the values specified in the model.
const equationCoords = model.lines?.get(instanceKey)?.currentEquationCoords;
if (equationCoords) {
const left = equationCoords.x * 100,
top = equationCoords.y * 100;
equationP.style('left', `${left}%`)
.style('top', `${top}%`);
}

newLineObject.equation = equationDiv;
setLineObject(newLineObject);

Expand Down

0 comments on commit f2a0839

Please sign in to comment.