Skip to content
Snippets Groups Projects
Commit 5c53094b authored by Remi Lazarini's avatar Remi Lazarini
Browse files

[Ivette] Graph : color management

parent fca1b319
No related branches found
No related tags found
No related merge requests found
/* ************************************************************************ */
/* */
/* This file is part of Frama-C. */
/* */
/* Copyright (C) 2007-2024 */
/* CEA (Commissariat à l'énergie atomique et aux énergies */
/* alternatives) */
/* */
/* you can redistribute it and/or modify it under the terms of the GNU */
/* Lesser General Public License as published by the Free Software */
/* Foundation, version 2.1. */
/* */
/* It is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU Lesser General Public License for more details. */
/* */
/* See the GNU Lesser General Public License version 2.1 */
/* for more details (enclosed in the file licenses/LGPLv2.1). */
/* */
/* ************************************************************************ */
/**
@packageDocumentation
@module dome/colors
*/
import React from 'react';
import { useStyle, useColorTheme } from 'dome/themes';
export enum EColor {
DEFAULT = "default",
WHITE = 'white',
GREY = 'grey',
DARK = 'dark',
PRIMARY = 'primary',
SELECTED = 'selected',
GREEN = 'green',
ORANGE = 'orange',
RED = 'red',
YELLOW = 'yellow',
BLUE = 'blue',
PINK = 'pink'
}
export type TColor = `${EColor}`
type TColorVal = {
[key in EColor]: string
}
export interface IHookColors {
BGCOLOR: TColorVal;
SGCOLOR: TColorVal;
FGCOLOR: TColorVal;
EDCOLOR: TColorVal;
}
type TColorCategory = 'bg'|'fg'|'sg'|'ed';
export function useColor(): IHookColors {
const style = useStyle();
const [theme, ] = useColorTheme();
const enum2Tcolor = (callback: (elt: EColor) => string): TColorVal => {
return {
...(Object.fromEntries(
Object.values(EColor).map((val: EColor) => [ val, callback(val)])
)),
} as TColorVal;
};
function getColorType(type: TColorCategory): TColorVal {
return enum2Tcolor(
(elt) => style.getPropertyValue('--graph-'+type+'-color-'+elt)
);
}
const colors = React.useMemo(() => {
return {
// node background colors
BGCOLOR: getColorType('bg'),
// // cluster background colors
SGCOLOR: getColorType('sg'),
// foreground colors
FGCOLOR: getColorType('fg'),
// // edge colors
EDCOLOR: getColorType('ed'),
};
},
/** style is dependent on theme but it is not used directly */
// eslint-disable-next-line react-hooks/exhaustive-deps
[theme]
);
return colors;
}
......@@ -125,8 +125,58 @@
--status-considered-valid: linear-gradient(to right, var(--status-valid) 49%, #73BBBB 51%);
--status-invalid-under-hyp: linear-gradient(to right, var(--status-invalid) 49%, var(--status-unknown) 51%);
--graph-node-selected-color: #F4D03F;
--graph-node-color: #5DADE2;
/* node background colors */
--graph-bg-color-default: #4c596b;
--graph-bg-color-white: #fff;
--graph-bg-color-grey: #ccc;
--graph-bg-color-dark: #666;
--graph-bg-color-primary: dodgerblue;
--graph-bg-color-selected: deepskyblue;
--graph-bg-color-green: lime;
--graph-bg-color-orange: #ffa700;
--graph-bg-color-red: red;
--graph-bg-color-yellow: yellow;
--graph-bg-color-blue: cyan;
--graph-bg-color-pink: hotpink;
/* cluster background colors */
--graph-sg-color-default: #4c596b;
--graph-sg-color-white: #ccc;
--graph-sg-color-grey: #bbb;
--graph-sg-color-dark: #aaa;
--graph-sg-color-primary: #4fc3f7;
--graph-sg-color-selected: #4c596b;
--graph-sg-color-green: #AED581;
--graph-sg-color-orange: #FFCC80;
--graph-sg-color-red: #ff6e6e;
--graph-sg-color-yellow: #d4cc82;
--graph-sg-color-blue: #bbdefb;
--graph-sg-color-pink: #f8bbd0;
/** foreground colors */
--graph-fg-color-default: white;
--graph-fg-color-white: black;
--graph-fg-color-grey: black;
--graph-fg-color-dark: white;
--graph-fg-color-primary: white;
--graph-fg-color-selected: black;
--graph-fg-color-green: black;
--graph-fg-color-orange: black;
--graph-fg-color-red: white;
--graph-fg-color-yellow: black;
--graph-fg-color-blue: black;
--graph-fg-color-pink: white;
/** edge colors */
--graph-ed-color-default: white;
--graph-ed-color-white: #fff;
--graph-ed-color-grey: #ddd;
--graph-ed-color-dark: black;
--graph-ed-color-primary: dodgerblue;
--graph-ed-color-selected: deepskyblue;
--graph-ed-color-green: green;
--graph-ed-color-orange: orange;
--graph-ed-color-red: red;
--graph-ed-color-yellow: #e5e100;
--graph-ed-color-blue: deepskyblue;
--graph-ed-color-pink: palevioletred1;
}
}
......@@ -23,6 +23,7 @@
import React from 'react';
import { Catch } from 'dome/errors';
import { classes } from 'dome/misc/utils';
import { useColor, IHookColors, TColor, EColor } from '../colors';
import { Size } from 'react-virtualized';
import { select, selectAll } from 'd3-selection';
import { graphviz } from 'd3-graphviz';
......@@ -37,12 +38,6 @@ export type Direction = 'LR' | 'TD';
export type Font = 'serif' | 'sans' | 'mono';
export type Color =
| 'white' | 'grey' | 'dark'
| 'primary' | 'selected'
| 'green' | 'orange' | 'red'
| 'yellow' | 'blue' | 'pink';
export type Shape =
| 'point' | 'box'
| 'diamond' | 'hexagon'
......@@ -72,7 +67,7 @@ export interface Node {
/** Node font (label) */
font?: Font;
/** Node color (filled background) */
color?: Color;
color?: TColor;
/**
* Shape. Nested boxes alternate LR and TD directions. Initial direction is
* orthogonal to the graph direction. Node label is ignored for box layout.
......@@ -96,7 +91,7 @@ export interface Edge {
/** Default is `solid` */
line?: Line;
/** Default is `dark` */
color?: Color;
color?: TColor;
head?: Arrow;
headLabel?: string,
headAnchor?: Anchor;
......@@ -120,7 +115,7 @@ export interface Cluster {
/** Title (default is none) */
title?: string;
/** Background color (default is grey) */
color?: Color;
color?: TColor;
}
/* -------------------------------------------------------------------------- */
......@@ -152,7 +147,6 @@ export interface DiagramProps {
/** Debug the generated DotModel */
onModelChanged?: (model: string) => void;
}
/* -------------------------------------------------------------------------- */
......@@ -165,66 +159,6 @@ const FONTNAME = {
'mono': 'Courier',
};
// node background colors
const BGCOLOR = {
'white': '#fff',
'grey': '#ccc',
'dark': '#666',
'primary': 'dodgerblue',
'selected': 'deepskyblue',
'green': 'lime',
'orange': '#ffa700',
'red': 'red',
'yellow': 'yellow',
'blue': 'cyan',
'pink': 'hotpink',
};
// cluster background colors
const SGCOLOR = {
'white': '#eee',
'grey': '#ccc',
'dark': '#aaa',
'primary': '#4fc3f7',
'selected': '#90caf9',
'green': '#AED581',
'orange': '#FFCC80',
'red': '#ff6e6e',
'yellow': '#fff59d',
'blue': '#bbdefb',
'pink': '#f8bbd0',
};
// foreground colors
const FGCOLOR = {
'white': 'black',
'grey': 'black',
'dark': 'white',
'primary': 'white',
'selected': 'black',
'green': 'black',
'orange': 'black',
'red': 'white',
'yellow': 'black',
'blue': 'black',
'pink': 'white',
};
// edge colors
const EDCOLOR = {
'white': '#ccc',
'grey': '#888',
'dark': 'black',
'primary': 'dodgerblue',
'selected': 'deepskyblue',
'green': 'green',
'orange': 'orange',
'red': 'red',
'yellow': '#e5e100',
'blue': 'deepskyblue',
'pink': 'palevioletred1',
};
/* -------------------------------------------------------------------------- */
/* --- Edge Model --- */
/* -------------------------------------------------------------------------- */
......@@ -242,6 +176,7 @@ type cluster = { props: Cluster; nodes: Node[]; }
class Builder {
private colors: IHookColors;
private selected: string | undefined;
private spec = '';
......@@ -250,6 +185,10 @@ class Builder {
private rmap = new Map<string, string>();
private cmap = new Map<string, cluster>();
constructor(colors: IHookColors) {
this.colors = colors;
}
index(id: string): string {
const n = this.imap.get(id);
if (n !== undefined) return n;
......@@ -372,23 +311,24 @@ class Builder {
.attr('shape', n.shape)
.attr('tooltip', n.title ?? n.label ?? n.id);
}
const color = n.color ?? (n.id === this.selected ? 'selected' : 'white');
const color = n.color ??
( n.id === this.selected ? EColor.SELECTED : EColor.DEFAULT );
this
.attr('fontcolor', FGCOLOR[color])
.attr('fillcolor', BGCOLOR[color])
.attr('fontcolor', this.colors.FGCOLOR[color])
.attr('fillcolor', this.colors.BGCOLOR[color])
.println(' ];');
}
cluster(c: cluster): void {
const { props: s, nodes } = c;
const { color = 'grey' } = s;
const { color = EColor.GREY } = s;
this
.print(' subgraph cluster_', this.index(s.id), ' {\n ')
.attr('style', 'filled')
.attr('label', s.label)
.attr('tooltip', s.title ?? s.id)
.attr('fontcolor', FGCOLOR[color])
.attr('fillcolor', SGCOLOR[color])
.attr('fontcolor', this.colors.FGCOLOR[color])
.attr('fillcolor', this.colors.SGCOLOR[color])
.print('\n ');
nodes.forEach(n => this.print(' ', this.index(n.id), ';'));
this.println('\n }');
......@@ -433,7 +373,12 @@ class Builder {
.attr('tailtooltip', e.tailLabel ? tooltip : undefined)
.attr('tooltip', tooltip)
.attr('dir', DIR(head, tail))
.attr('color', e.color ? EDCOLOR[e.color] : undefined)
.attr('color', e.color ?
this.colors.EDCOLOR[e.color] :
this.colors.EDCOLOR[EColor.DEFAULT])
.attr('fontcolor', e.color ?
this.colors.EDCOLOR[e.color] :
this.colors.EDCOLOR[EColor.DEFAULT])
.attr('style', line === 'solid' ? undefined : line)
.attr('arrowhead', head === 'arrow' ? undefined : head)
.attr('arrowtail', tail === 'arrow' ? undefined : tail)
......@@ -457,9 +402,11 @@ const newDivId = (): string => `dome_xDiagram_g${++divId}`;
interface GraphvizProps extends DiagramProps { size: Size }
function GraphvizView(props: GraphvizProps): JSX.Element {
// Colors
const colors = useColor();
// --- Builder Instance (unique)
const builder = React.useMemo(() => new Builder, []);
const builder = React.useMemo(() => new Builder(colors), [colors]);
// --- Model Generation
const {
......
......@@ -36,9 +36,10 @@ import ForceGraph3D, {
ForceGraphProps as ForceGraphProps3D,
} from 'react-force-graph-3d';
/** Three is a dependency of 3d-force-graph */
import {
CSS2DRenderer, CSS2DObject
} from "three/examples/jsm/renderers/CSS2DRenderer";
} from 'three/examples/jsm/renderers/CSS2DRenderer';
import { Size } from 'react-virtualized';
import AutoSizer from 'react-virtualized-auto-sizer';
......@@ -50,6 +51,7 @@ import * as Themes from 'dome/themes';
/* --- Graph Specifications --- */
/* -------------------------------------------------------------------------- */
/** Type layout */
export type Layout = '2D' | '3D';
export interface Node {
......@@ -68,15 +70,15 @@ export interface Edge {
/* --- Force Graph Components --- */
/* -------------------------------------------------------------------------- */
export interface GNode {
interface GNode {
id: string;
label?: string;
}
export interface GLink {
interface GLink {
source: string;
target: string;
}
export interface GData {
interface GData {
nodes: GNode[];
links: GLink[];
}
......@@ -92,7 +94,7 @@ interface IGProps2D extends IGProps {
options?: IGraphOptions;
}
export interface IGProps3D extends IGProps {
interface IGProps3D extends IGProps {
options?: IGraphOptions3D;
}
......@@ -133,7 +135,7 @@ export interface IGraphOptions {
* If displayMode = td, the graph will be display like a tree from top to down
* Work only if the graph has no cycle
*/
displayMode?: "td";
displayMode?: 'td';
/** Spacing between depths level */
depthSpacing?: number;
/** A string[][] ref to save the cycles */
......@@ -277,8 +279,8 @@ function Graph2D(props: IGProps2D): JSX.Element {
}}
cooldownTime={50}
nodeColor={(node) => (node.id === selected ?
style.getPropertyValue("--graph-node-selected-color") :
style.getPropertyValue("--graph-node-color"))
style.getPropertyValue('--graph-bg-color-selected') :
style.getPropertyValue('--graph-bg-color-orange'))
}
/>
);
......@@ -333,7 +335,7 @@ function getForceGraphOptions(
return ret;
}
export function Graph3D(props: IGProps3D): JSX.Element {
function Graph3D(props: IGProps3D): JSX.Element {
const { data, onSelection, selected, size,
options = {}
} = props;
......@@ -348,8 +350,7 @@ export function Graph3D(props: IGProps3D): JSX.Element {
const style = Themes.useStyle();
const [ , flipHorizontalSpacingIsSet ] =
Dome.useFlipSettings("ivette.callgraph.horizontalSpacingIsSet", true);
Dome.useFlipSettings('ivette.callgraph.horizontalSpacingIsSet', true);
React.useEffect(() => {
if (fgRef3D.current && selected) {
......@@ -413,8 +414,8 @@ export function Graph3D(props: IGProps3D): JSX.Element {
node.fz = node.z;
}}
nodeColor={(node) => (node.id === selected ?
style.getPropertyValue("--graph-node-selected-color") :
style.getPropertyValue("--graph-node-color"))
style.getPropertyValue('--graph-bg-color-selected') :
style.getPropertyValue('--graph-bg-color-orange'))
}
{...graphOptions}
/>
......
......@@ -3,5 +3,5 @@
/* -------------------------------------------------------------------------- */
.dome-xDiagram {
background: #eee;
background: var(--background);
}
......@@ -125,7 +125,57 @@
--status-considered-valid: linear-gradient(to right, var(--status-valid) 49%, #73BBBB 51%);
--status-invalid-under-hyp: linear-gradient(to right, var(--status-invalid) 49%, var(--status-unknown) 51%);
--graph-node-selected-color: #F4D03F;
--graph-node-color: #5DADE2;
/* node background colors */
--graph-bg-color-default: #fff;
--graph-bg-color-white: #fff;
--graph-bg-color-grey: #ccc;
--graph-bg-color-dark: #666;
--graph-bg-color-primary: dodgerblue;
--graph-bg-color-selected: deepskyblue;
--graph-bg-color-green: lime;
--graph-bg-color-orange: #ffa700;
--graph-bg-color-red: red;
--graph-bg-color-yellow: yellow;
--graph-bg-color-blue: cyan;
--graph-bg-color-pink: hotpink;
/* cluster background colors */
--graph-sg-color-default: #eee;
--graph-sg-color-white: #eee;
--graph-sg-color-grey: #ccc;
--graph-sg-color-dark: #aaa;
--graph-sg-color-primary: #4fc3f7;
--graph-sg-color-selected: #90caf9;
--graph-sg-color-green: #AED581;
--graph-sg-color-orange: #FFCC80;
--graph-sg-color-red: #ff6e6e;
--graph-sg-color-yellow: #fff59d;
--graph-sg-color-blue: #bbdefb;
--graph-sg-color-pink: #f8bbd0;
/** foreground colors */
--graph-fg-color-default: black;
--graph-fg-color-white: black;
--graph-fg-color-grey: black;
--graph-fg-color-dark: white;
--graph-fg-color-primary: white;
--graph-fg-color-selected: black;
--graph-fg-color-green: black;
--graph-fg-color-orange: black;
--graph-fg-color-red: white;
--graph-fg-color-yellow: black;
--graph-fg-color-blue: black;
--graph-fg-color-pink: white;
/** edge colors */
--graph-ed-color-default: black;
--graph-ed-color-white: #ccc;
--graph-ed-color-grey: #888;
--graph-ed-color-dark: black;
--graph-ed-color-primary: dodgerblue;
--graph-ed-color-selected: deepskyblue;
--graph-ed-color-green: green;
--graph-ed-color-orange: orange;
--graph-ed-color-red: red;
--graph-ed-color-yellow: #e5e100;
--graph-ed-color-blue: deepskyblue;
--graph-ed-color-pink: palevioletred1;
}
}
......@@ -89,7 +89,10 @@ const edges: Edge[] = [
function makeCluster(s: string | undefined): Cluster {
const color = nodes.find(n => n.id === s)?.color;
return { id: 'BG', title: 'Background Cluster', color };
return {
id: 'BG',
title: 'Background Cluster',
color: color ?? "default" };
}
function DiagramSample(): JSX.Element {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment