Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
Signed-off-by: rashidakanchwala <[email protected]>
  • Loading branch information
rashidakanchwala committed Jan 28, 2025
1 parent 160dbec commit 8abae8d
Show file tree
Hide file tree
Showing 13 changed files with 302 additions and 152 deletions.
13 changes: 13 additions & 0 deletions src/actions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,19 @@ export function toggleLayers(visible) {
};
}

export const TOGGLE_ORIENTATION = 'TOGGLE_ORIENTATION';

/**
* Toggle whether to show horizontal or vertical orientation
* @param {string} orientation The orientation to set to vertical by default
*/
export function toggleOrientation(orientation) {
return {
type: TOGGLE_ORIENTATION,
orientation,
};
}

export const TOGGLE_EXPAND_ALL_PIPELINES = 'TOGGLE_EXPAND_ALL_PIPELINES';

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
toggleSidebar,
toggleTextLabels,
toggleExpandAllPipelines,
toggleOrientation,
} from '../../actions';
import { toggleModularPipelinesVisibilityState } from '../../actions/modular-pipelines';
import IconButton from '../ui/icon-button';
Expand Down Expand Up @@ -35,6 +36,8 @@ export const FlowchartPrimaryToolbar = ({
visibleLayers,
expandedPipelines,
onToggleExpandAllPipelines,
orientation,
onToggleOrientation,
}) => {
const { toSetQueryParam } = useGeneratePathname();

Expand Down Expand Up @@ -97,6 +100,19 @@ export const FlowchartPrimaryToolbar = ({
onClick={() => onToggleExportModal(true)}
visible={display.exportBtn}
/>
<IconButton
ariaLabel="Change flowchart orientation"
className={'pipeline-menu-button--orientation'}
dataTest={'sidebar-flowchart-orientation-btn'}
icon={ExportIcon}
labelText="Change Orientation"
onClick={() =>
onToggleOrientation(
orientation === 'vertical' ? 'horizontal' : 'vertical'
)
}
visible={display.orientationBtn}
/>
</PrimaryToolbar>
</>
);
Expand All @@ -108,6 +124,7 @@ export const mapStateToProps = (state) => ({
visible: state.visible,
display: state.display,
visibleLayers: Boolean(getVisibleLayerIDs(state).length),
orientation: state.orientation,
expandedPipelines: state.expandAllPipelines,
});

Expand All @@ -128,6 +145,9 @@ export const mapDispatchToProps = (dispatch) => ({
dispatch(toggleExpandAllPipelines(isExpanded));
dispatch(toggleModularPipelinesVisibilityState(isExpanded));
},
onToggleOrientation: (value) => {
dispatch(toggleOrientation(value));
},
});

export default connect(
Expand Down
2 changes: 2 additions & 0 deletions src/reducers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
TOGGLE_IS_PRETTY_NAME,
TOGGLE_TEXT_LABELS,
TOGGLE_THEME,
TOGGLE_ORIENTATION,
UPDATE_CHART_SIZE,
UPDATE_ZOOM,
TOGGLE_EXPAND_ALL_PIPELINES,
Expand Down Expand Up @@ -96,6 +97,7 @@ const combinedReducer = combineReducers({
zoom: createReducer({}, UPDATE_ZOOM, 'zoom'),
textLabels: createReducer(true, TOGGLE_TEXT_LABELS, 'textLabels'),
theme: createReducer('dark', TOGGLE_THEME, 'theme'),
orientation: createReducer('vertical', TOGGLE_ORIENTATION, 'orientation'),
isPrettyName: createReducer(false, TOGGLE_IS_PRETTY_NAME, 'isPrettyName'),
showFeatureHints: createReducer(
true,
Expand Down
58 changes: 41 additions & 17 deletions src/selectors/layers.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ import { getVisibleLayerIDs } from './disabled';

const getGraph = (state) => state.graph;
const getLayerName = (state) => state.layer.name;
const getFlowChartOrientation = (state) => state.orientation;

/**
* Get layer positions
*/
export const getLayers = createSelector(
[getGraph, getVisibleLayerIDs, getLayerName],
({ nodes, size }, layerIDs, layerName) => {
[getGraph, getVisibleLayerIDs, getLayerName, getFlowChartOrientation],
({ nodes, size }, layerIDs, layerName, orientation) => {
if (!nodes || !size || !nodes.length || !layerIDs.length) {
return [];
}
Expand All @@ -23,12 +24,22 @@ export const getLayers = createSelector(
if (layer) {
const bound = bounds[layer] || (bounds[layer] = [Infinity, -Infinity]);

if (node.y - node.height < bound[0]) {
bound[0] = node.y - node.height;
}
if (orientation === 'vertical') {
if (node.x - node.width < bound[0]) {
bound[0] = node.x - node.width;
}

if (node.x + node.width > bound[1]) {
bound[1] = node.x + node.width;
}
} else {
if (node.y - node.height < bound[0]) {
bound[0] = node.y - node.height;
}

if (node.y + node.height > bound[1]) {
bound[1] = node.y + node.height;
if (node.y + node.height > bound[1]) {
bound[1] = node.y + node.height;
}
}
}
}
Expand All @@ -45,16 +56,29 @@ export const getLayers = createSelector(
];
const start = (prevBound[1] + currentBound[0]) / 2;
const end = (currentBound[1] + nextBound[0]) / 2;
const rectWidth = Math.max(width, height) * 5;

return {
id,
name: layerName[id],
x: (rectWidth - width) / -2,
y: start,
width: rectWidth,
height: Math.max(end - start, 0),
};
const rectSize = Math.max(width, height) * 5;

if (orientation === 'vertical') {
// Vertical layout when orientation is true
return {
id,
name: layerName[id],
x: start, // Horizontal layout moves along the x-axis
y: (rectSize - height) / -2, // Centered along y-axis
width: Math.max(end - start, 0),
height: rectSize,
};
} else {
// Horizontal layout when orientation is false
return {
id,
name: layerName[id],
y: start, // Vertical layout moves along the y-axis
x: (rectSize - width) / -2, // Centered along x-axis
height: Math.max(end - start, 0),
width: rectSize,
};
}
});
}
);
6 changes: 4 additions & 2 deletions src/selectors/layout.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const getVisibleCode = (state) => state.visible.code;
const getIgnoreLargeWarning = (state) => state.ignoreLargeWarning;
const getGraphHasNodes = (state) => Boolean(state.graph?.nodes?.length);
const getChartSizeState = (state) => state.chartSize;
const getFlowChartOrientation = (state) => state.orientation;

/**
* Show the large graph warning only if there are sufficient nodes + edges,
Expand Down Expand Up @@ -50,14 +51,15 @@ export const getGraphInput = createSelector(
getVisibleNodes,
getVisibleEdges,
getVisibleLayerIDs,
getFlowChartOrientation,
getTriggerLargeGraphWarning,
],
(nodes, edges, layers, triggerLargeGraphWarning) => {
(nodes, edges, layers, orientation, triggerLargeGraphWarning) => {
if (triggerLargeGraphWarning) {
return null;
}

return { nodes, edges, layers };
return { nodes, edges, layers, orientation };
}
);

Expand Down
2 changes: 2 additions & 0 deletions src/store/initial-state.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export const createInitialState = () => ({
textLabels: true,
theme: 'dark',
expandAllPipelines: false,
orientation: 'vertical',
isPrettyName: settings.isPrettyName.default,
showFeatureHints: settings.showFeatureHints.default,
showDatasetPreviews: settings.showDatasetPreviews.default,
Expand Down Expand Up @@ -55,6 +56,7 @@ export const createInitialState = () => ({
exportBtn: true,
labelBtn: true,
layerBtn: true,
orientationBtn: true,
zoomToolbar: true,
metadataPanel: true,
},
Expand Down
29 changes: 21 additions & 8 deletions src/utils/graph/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,19 @@ export const snap = (value, unit) => Math.round(value / unit) * unit;
export const distance1d = (a, b) => Math.abs(a - b);

/**
* Returns the angle in radians between the points a and b relative to the X-axis about the origin
* Returns the angle in radians between the points a and b based on the given orientation
* @param {Object} a The first point
* @param {Object} b The second point
* @param {String} orientation The layout orientation
* @returns {Number} The angle
*/
export const angle = (a, b) => Math.atan2(a.y - b.y, a.x - b.x);
export const angle = (a, b, orientation) => {
if (orientation === 'vertical') {
return Math.atan2(a.y - b.y, a.x - b.x);
} else {
return Math.atan2(a.x - b.x, a.y - b.y);
}
};

/**
* Returns the left edge x-position of the node
Expand Down Expand Up @@ -75,23 +82,29 @@ export const nodeBottom = (node) => node.y + node.height * 0.5;
* @param {Array} nodes The input nodes
* @returns {Array} The sorted rows of nodes
*/
export const groupByRow = (nodes) => {
export const groupByRow = (nodes, orientation) => {
const rows = {};

// Create rows using node Y values
const primaryCoord = orientation === 'vertical' ? 'x' : 'y';
const secondaryCoord = orientation === 'horizontal' ? 'y' : 'x';

// Create rows using primaryCoord values
for (const node of nodes) {
rows[node.y] = rows[node.y] || [];
rows[node.y].push(node);
const key = snap(node[primaryCoord], 10);
rows[key] = rows[key] || [];
rows[key].push(node);
}

// Sort the set of rows accounting for keys being strings
const rowNumbers = Object.keys(rows).map((row) => parseFloat(row));
rowNumbers.sort((a, b) => a - b);

// Sort rows in order of X position if set. Break ties with ids for stability
// Sort rows in order of secondaryCoord position if set. Break ties with ids for stability
const sortedRows = rowNumbers.map((row) => rows[row]);
for (let i = 0; i < sortedRows.length; i += 1) {
sortedRows[i].sort((a, b) => compare(a.x, b.x, a.id, b.id));
sortedRows[i].sort((a, b) =>
compare(a[secondaryCoord], b[secondaryCoord], a.id, b.id)
);

for (const node of sortedRows[i]) {
node.row = i;
Expand Down
41 changes: 19 additions & 22 deletions src/utils/graph/constraints.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,11 @@ import { Constraint, Operator, Strength } from 'kiwi.js';
* Layout constraint in Y for separating rows
*/
export const rowConstraint = {
property: 'y',

strict: (constraint, constants, variableA, variableB) =>
new Constraint(
variableA.minus(variableB),
Operator.Ge,
constants.spaceY,
constraint.separation,
Strength.required
),
};
Expand All @@ -27,8 +25,6 @@ export const rowConstraint = {
* Layout constraint in Y for separating layers
*/
export const layerConstraint = {
property: 'y',

strict: (constraint, constants, variableA, variableB) =>
new Constraint(
variableA.minus(variableB),
Expand All @@ -42,13 +38,12 @@ export const layerConstraint = {
* Layout constraint in X for minimising distance from source to target for straight edges
*/
export const parallelConstraint = {
property: 'x',

solve: (constraint) => {
solve: (constraint, constants) => {
const { a, b, strength } = constraint;
const resolve = strength * (a.x - b.x);
a.x -= resolve;
b.x += resolve;
const resolve =
strength * (a[constants.coordPrimary] - b[constants.coordPrimary]);
a[constants.coordPrimary] -= resolve;
b[constants.coordPrimary] += resolve;
},

strict: (constraint, constants, variableA, variableB) =>
Expand All @@ -64,34 +59,36 @@ export const parallelConstraint = {
* Crossing constraint in X for minimising edge crossings
*/
export const crossingConstraint = {
property: 'x',

solve: (constraint) => {
solve: (constraint, constants) => {
const { edgeA, edgeB, separationA, separationB, strength } = constraint;

// Amount to move each node towards required separation
const resolveSource =
strength *
((edgeA.sourceNode.x - edgeB.sourceNode.x - separationA) / separationA);
((edgeA.sourceNode[constants.coordPrimary] -
edgeB.sourceNode[constants.coordPrimary] -
separationA) /
separationA);

const resolveTarget =
strength *
((edgeA.targetNode.x - edgeB.targetNode.x - separationB) / separationB);
((edgeA.targetNode[constants.coordPrimary] -
edgeB.targetNode[constants.coordPrimary] -
separationB) /
separationB);

// Apply the resolve each node
edgeA.sourceNode.x -= resolveSource;
edgeB.sourceNode.x += resolveSource;
edgeA.targetNode.x -= resolveTarget;
edgeB.targetNode.x += resolveTarget;
edgeA.sourceNode[constants.coordPrimary] -= resolveSource;
edgeB.sourceNode[constants.coordPrimary] += resolveSource;
edgeA.targetNode[constants.coordPrimary] -= resolveTarget;
edgeB.targetNode[constants.coordPrimary] += resolveTarget;
},
};

/**
* Layout constraint in X for minimum node separation
*/
export const separationConstraint = {
property: 'x',

strict: (constraint, constants, variableA, variableB) =>
new Constraint(
variableB.minus(variableA),
Expand Down
Loading

0 comments on commit 8abae8d

Please sign in to comment.