From 861b0b1bfda20efe29253728c0073aa70875756f Mon Sep 17 00:00:00 2001 From: Mauro Bianchi Date: Wed, 3 Jun 2020 19:59:23 +0200 Subject: [PATCH] fixed dataset. coloring lines, no zoom for now --- src/visualizations/Trama2/LineeTrama.js | 336 +++++++++++------- src/visualizations/Trama2/Trama2.css | 25 +- src/visualizations/Trama2/Trama2Main.js | 19 +- .../Trama2/dati/ordine_colore_y.json | 56 +-- 4 files changed, 272 insertions(+), 164 deletions(-) diff --git a/src/visualizations/Trama2/LineeTrama.js b/src/visualizations/Trama2/LineeTrama.js index 12d6b33b..f1ddaca3 100644 --- a/src/visualizations/Trama2/LineeTrama.js +++ b/src/visualizations/Trama2/LineeTrama.js @@ -1,166 +1,238 @@ -import React, { useRef, useState, useMemo, useEffect, useCallback } from 'react' -import { line, curveMonotoneX } from 'd3-shape' -import { scaleLinear } from 'd3-scale' - - -const MAX_ZOOM_LEVEL = 10 - - - - -function LineaTrama({racconto, data, xScale, width, height, zoomLevel, index}){ - const lineGenerator = line().x(d => d.x).y(d => d.y * zoomLevel).curve(curveMonotoneX) - const d = lineGenerator(data) - return - {/* */} - - {index} - {/* */} +import React, { + useRef, + useState, + useMemo, + useEffect, + useCallback, +} from "react"; +import { line, curveMonotoneX } from "d3-shape"; +import { scaleLinear } from "d3-scale"; + +const MAX_ZOOM_LEVEL = 10; + +function LineaTrama({ + racconto, + data, + xScale, + width, + height, + zoomLevel, + index, + gradient, + itemSelected, + toggleItem +}) { + const lineGenerator = line() + .x((d) => d.x) + .y((d) => d.y * zoomLevel) + .curve(curveMonotoneX); + const d = lineGenerator(data); + const stroke = itemSelected ? `url('#trama-gradient-${zoomLevel}')` : '#ddd' + return ( - {data.map((d,i) => ())} + {/* */} + + + + {index} + + {/* */} + {itemSelected && + {data.map((d, i) => ( + + ))} + } - + ); } +export default function LineeTrama({ + racconti = [], + data = {}, + height, + scalaColore, + scalaMotivoY, + colors, + selected, + toggleSelect +}) { + const containerRef = useRef(null); + const [measures, setMeasures] = useState(null); + const [zoomLevel, setZoomLevel] = useState(1.0); + const [zoomY, setZoomY] = useState(0); + + console.log("colors", colors); -export default function LineeTrama({racconti=[], data={}, height, scalaColore, scalaMotivoY}){ - + useEffect(() => { + const m = containerRef.current.getBoundingClientRect(); + setMeasures(m); + }, []); - const containerRef = useRef(null) - const [measures, setMeasures] = useState(null) - const [zoomLevel, setZoomLevel] = useState(1.0) - const [zoomY, setZoomY] = useState(0) + const scrollHandler = useCallback( + (e, n) => { + const sign = e.deltaY > 0 ? 1.0 : -1.0; - - - useEffect(() => { - const m = containerRef.current.getBoundingClientRect() - setMeasures(m) - }, []) - - const scrollHandler = useCallback((e, n) => { - - - const sign = e.deltaY > 0 ? 1.0 : -1.0 - - const newLevel = zoomLevel + (0.1 * sign) - - if(newLevel > MAX_ZOOM_LEVEL){ - return + const newLevel = zoomLevel + 0.1 * sign; + + if (newLevel > MAX_ZOOM_LEVEL) { + return; } - if(newLevel <= 1){ - setZoomY(0) - return + if (newLevel <= 1) { + setZoomY(0); + return; } - setZoomLevel(Math.max(1, Math.min(newLevel, MAX_ZOOM_LEVEL))) - if(!zoomY){ - setZoomY(e.layerY) + setZoomLevel(Math.max(1, Math.min(newLevel, MAX_ZOOM_LEVEL))); + if (!zoomY) { + setZoomY(e.layerY); } - - - return false - - - }, [zoomLevel, zoomY]) - useEffect(() => { - const n = containerRef.current + return false; + }, + [zoomLevel, zoomY] + ); - const sh = (e) => {scrollHandler(e, n)} - - n.addEventListener('wheel', sh, {passive: true}); - return () => { - n.removeEventListener('wheel', sh, {passive: true}); - } - - }, [scrollHandler]) - + // useEffect(() => { + // const n = containerRef.current; - const delta = useMemo(() => { - if(!measures){ return 0} - return (measures.height - height) / racconti.length + // const sh = (e) => { + // scrollHandler(e, n); + // }; - }, [height, measures, racconti.length]) + // n.addEventListener("wheel", sh, { passive: true }); + // return () => { + // n.removeEventListener("wheel", sh, { passive: true }); + // }; + // }, [scrollHandler]); - const xScale = useMemo(() => { - if(!measures){ return null} - return scaleLinear().domain([0, 1]).range([0, measures.width]) - }, [measures]) + const delta = useMemo(() => { + if (!measures) { + return 0; + } + return (measures.height - height) / racconti.length; + }, [height, measures, racconti.length]); + const xScale = useMemo(() => { + if (!measures) { + return null; + } + return scaleLinear().domain([0, 1]).range([0, measures.width]); + }, [measures]); const dataRacconti = useMemo(() => { - if(!xScale){ - return [] + if (!xScale) { + return []; } - return racconti.map(racconto => { - return data[racconto.titolo].filter(d => !!d.y).map(d => { - return { - x: xScale(d.x), - y: d.y - } - }) - }) - }, [data, racconti, xScale]) + return racconti.map((racconto) => { + return data[racconto.titolo] + .filter((d) => !!d.y) + .map((d) => { + return { + x: xScale(d.x), + y: d.y, + }; + }); + }); + }, [data, racconti, xScale]); - const baseDisplacements = useMemo(() => { return dataRacconti.map((d, i) => { let dy = delta * i; - return dy - }) - }, [dataRacconti, delta]) + return dy; + }); + }, [dataRacconti, delta]); - const displacements = useMemo(() => { return dataRacconti.map((d, i) => { - let dy = baseDisplacements[i] + let dy = baseDisplacements[i]; - const j = Math.round(zoomY/delta) + const j = Math.round(zoomY / delta); - const diff = delta * j * zoomLevel - const factor = (zoomLevel - 1) / (MAX_ZOOM_LEVEL - 1) * zoomLevel + const diff = delta * j * zoomLevel; + const factor = ((zoomLevel - 1) / (MAX_ZOOM_LEVEL - 1)) * zoomLevel; - return dy * zoomLevel - diff * factor - }) - }, [baseDisplacements, dataRacconti, delta, zoomLevel, zoomY]) + return dy * zoomLevel - diff * factor; + }); + }, [baseDisplacements, dataRacconti, delta, zoomLevel, zoomY]); + const deltaColors = useMemo(() => { + return ( height * zoomLevel) / colors.length + }, [colors.length, height, zoomLevel]) - return
- {measures && - {dataRacconti.map((datum, i) => { - - - const dy = displacements[i] - + // - if(dy < - height * zoomLevel){ - return null - } - if(dy > measures.height){ - return null - } - - - // const datum = data[racconto] - return - - - })} - - } -
- - -} \ No newline at end of file + + + + return ( +
+ {measures && ( + + + {colors.map((color, i) => ( + + ))} + + + {dataRacconti.map((datum, i) => { + const dy = displacements[i]; + + if (dy < -height * zoomLevel) { + return null; + } + if (dy > measures.height) { + return null; + } + + // const datum = data[racconto] + return ( + + + + ); + })} + + )} +
+ ); +} diff --git a/src/visualizations/Trama2/Trama2.css b/src/visualizations/Trama2/Trama2.css index ad03d04b..9cd40e92 100644 --- a/src/visualizations/Trama2/Trama2.css +++ b/src/visualizations/Trama2/Trama2.css @@ -58,13 +58,32 @@ } +.trama2-pointer { + /* stroke: #ddd; */ + fill: none; + stroke: transparent; + stroke-width: 3px; + cursor: pointer; + /* fill: none; */ +} + +.trama2-pointer:hover + .trama2-line:not(.selected){ + /* stroke: #ddd; */ + stroke: #000 !important; + /* fill: none; */ +} + .trama2-line { - stroke: #ddd; - stroke-width: 1px; + /* stroke: #ddd; */ fill: none; + stroke-width: 1px; + pointer-events: none; + /* fill: none; */ } + .trama2-circle { - fill: #ddd; + stroke: #ddd; + fill: #000; } \ No newline at end of file diff --git a/src/visualizations/Trama2/Trama2Main.js b/src/visualizations/Trama2/Trama2Main.js index 8b73e63f..047b7751 100644 --- a/src/visualizations/Trama2/Trama2Main.js +++ b/src/visualizations/Trama2/Trama2Main.js @@ -40,6 +40,11 @@ const colorsOrdered = sortBy(coloriPosizioni, item => +item.ordine) // const colorsExtent = extent(coloriPosizioni, item => +item.ordine) const scalaColore = scaleLinear().domain(colorsOrdered.map(item => item.ordine)).range(colorsOrdered.map(item => item.colori)) +console.log("ordineColore", ordineByCluster, ordineColore) +const colors = + sortBy(ordineColore.map(item => get(ordineByCluster, item["cluster tipologia"])), item => item.ordine).map(x => x.colori) + + let racconti = sortBy(uniqBy(datasetLines, item => item["titolo racconto"]).map(item => ({ titolo: item["titolo racconto"], anno: item["anno"], @@ -52,7 +57,7 @@ let racconti = sortBy(uniqBy(datasetLines, item => item["titolo racconto"]).map( }) //#TODO: remove this (limiting for debug) -racconti = take(racconti,20) +racconti = take(racconti,100) const datasetLinesNormalized = datasetLines.map((item) => { @@ -90,6 +95,14 @@ export default function Trama2Main() { setSidePanelOpen(!sidePanelOpen); }, [sidePanelOpen]); + + const [selected, setSelected] = useState({}) + const toggleSelect= useCallback((index) => () => { + const newSelected = {...selected, [index]: !!!selected[index]} + setSelected(newSelected) + }, [selected]) + + return (
@@ -104,9 +117,13 @@ export default function Trama2Main() {
diff --git a/src/visualizations/Trama2/dati/ordine_colore_y.json b/src/visualizations/Trama2/dati/ordine_colore_y.json index 5aaf28f8..65467be8 100644 --- a/src/visualizations/Trama2/dati/ordine_colore_y.json +++ b/src/visualizations/Trama2/dati/ordine_colore_y.json @@ -1,77 +1,77 @@ [ {"ordine cluster":"9","cluster tipologia":"eventi liminali","ordine tipologia":"37","tipologia":"morte"} , -{"ordine cluster":"","cluster tipologia":"","ordine tipologia":"36","tipologia":"rivelazione"} +{"ordine cluster":"","cluster tipologia":"eventi liminali","ordine tipologia":"36","tipologia":"rivelazione"} , {"ordine cluster":"8","cluster tipologia":"interazione","ordine tipologia":"35","tipologia":"guerra"} , -{"ordine cluster":"","cluster tipologia":"","ordine tipologia":"34","tipologia":"aggressione/scontro"} +{"ordine cluster":"","cluster tipologia":"interazione","ordine tipologia":"34","tipologia":"aggressione/scontro"} , -{"ordine cluster":"","cluster tipologia":"","ordine tipologia":"33","tipologia":"aiuto/salvataggio"} +{"ordine cluster":"","cluster tipologia":"interazione","ordine tipologia":"33","tipologia":"aiuto/salvataggio"} , -{"ordine cluster":"","cluster tipologia":"","ordine tipologia":"32","tipologia":"compito/missione"} +{"ordine cluster":"","cluster tipologia":"interazione","ordine tipologia":"32","tipologia":"compito/missione"} , -{"ordine cluster":"","cluster tipologia":"","ordine tipologia":"31","tipologia":"incontro animale"} +{"ordine cluster":"","cluster tipologia":"interazione","ordine tipologia":"31","tipologia":"incontro animale"} , -{"ordine cluster":"","cluster tipologia":"","ordine tipologia":"30","tipologia":"incontro di gruppo"} +{"ordine cluster":"","cluster tipologia":"interazione","ordine tipologia":"30","tipologia":"incontro di gruppo"} , -{"ordine cluster":"","cluster tipologia":"","ordine tipologia":"29","tipologia":"incontro femminile"} +{"ordine cluster":"","cluster tipologia":"interazione","ordine tipologia":"29","tipologia":"incontro femminile"} , -{"ordine cluster":"","cluster tipologia":"","ordine tipologia":"28","tipologia":"incontro maschile"} +{"ordine cluster":"","cluster tipologia":"interazione","ordine tipologia":"28","tipologia":"incontro maschile"} , -{"ordine cluster":"","cluster tipologia":"","ordine tipologia":"27","tipologia":"matrimonio"} +{"ordine cluster":"","cluster tipologia":"interazione","ordine tipologia":"27","tipologia":"matrimonio"} , -{"ordine cluster":"","cluster tipologia":"","ordine tipologia":"26","tipologia":"offerta"} +{"ordine cluster":"","cluster tipologia":"interazione","ordine tipologia":"26","tipologia":"offerta"} , -{"ordine cluster":"","cluster tipologia":"","ordine tipologia":"25","tipologia":"rifiuto"} +{"ordine cluster":"","cluster tipologia":"interazione","ordine tipologia":"25","tipologia":"rifiuto"} , -{"ordine cluster":"","cluster tipologia":"","ordine tipologia":"24","tipologia":"scena erotica"} +{"ordine cluster":"","cluster tipologia":"interazione","ordine tipologia":"24","tipologia":"scena erotica"} , -{"ordine cluster":"","cluster tipologia":"","ordine tipologia":"23","tipologia":"scommessa"} +{"ordine cluster":"","cluster tipologia":"interazione","ordine tipologia":"23","tipologia":"scommessa"} , -{"ordine cluster":"","cluster tipologia":"","ordine tipologia":"22","tipologia":"telefonata"} +{"ordine cluster":"","cluster tipologia":"interazione","ordine tipologia":"22","tipologia":"telefonata"} , {"ordine cluster":"7","cluster tipologia":"spostamento","ordine tipologia":"21","tipologia":"arrivo/ritorno"} , -{"ordine cluster":"","cluster tipologia":"","ordine tipologia":"20","tipologia":"fuga"} +{"ordine cluster":"","cluster tipologia":"spostamento","ordine tipologia":"20","tipologia":"fuga"} , -{"ordine cluster":"","cluster tipologia":"","ordine tipologia":"19","tipologia":"inseguimento/ricerca"} +{"ordine cluster":"","cluster tipologia":"spostamento","ordine tipologia":"19","tipologia":"inseguimento/ricerca"} , -{"ordine cluster":"","cluster tipologia":"","ordine tipologia":"18","tipologia":"partenza/sparizione"} +{"ordine cluster":"","cluster tipologia":"spostamento","ordine tipologia":"18","tipologia":"partenza/sparizione"} , -{"ordine cluster":"","cluster tipologia":"","ordine tipologia":"17","tipologia":"viaggio"} +{"ordine cluster":"","cluster tipologia":"spostamento","ordine tipologia":"17","tipologia":"viaggio"} , {"ordine cluster":"6","cluster tipologia":"intenzione","ordine tipologia":"16","tipologia":"iniziativa/piano"} , -{"ordine cluster":"","cluster tipologia":"","ordine tipologia":"15","tipologia":"ostacolo"} +{"ordine cluster":"","cluster tipologia":"intenzione","ordine tipologia":"15","tipologia":"ostacolo"} , -{"ordine cluster":"","cluster tipologia":"","ordine tipologia":"14","tipologia":"successo"} +{"ordine cluster":"","cluster tipologia":"intenzione","ordine tipologia":"14","tipologia":"successo"} , {"ordine cluster":"5","cluster tipologia":"stato euforico","ordine tipologia":"13","tipologia":"illusione/speranza"} , -{"ordine cluster":"","cluster tipologia":"","ordine tipologia":"12","tipologia":"innamoramento"} +{"ordine cluster":"","cluster tipologia":"stato euforico","ordine tipologia":"12","tipologia":"innamoramento"} , {"ordine cluster":"4","cluster tipologia":"stato disforico","ordine tipologia":"11","tipologia":"angoscia/delusione"} , -{"ordine cluster":"","cluster tipologia":"","ordine tipologia":"10","tipologia":"smarrimento/dubbio"} +{"ordine cluster":"","cluster tipologia":"stato disforico","ordine tipologia":"10","tipologia":"smarrimento/dubbio"} , {"ordine cluster":"3","cluster tipologia":"stato riflessivo","ordine tipologia":"9","tipologia":"visione"} , -{"ordine cluster":"","cluster tipologia":"","ordine tipologia":"8","tipologia":"riflessione"} +{"ordine cluster":"","cluster tipologia":"stato riflessivo","ordine tipologia":"8","tipologia":"riflessione"} , -{"ordine cluster":"","cluster tipologia":"","ordine tipologia":"7","tipologia":"mistero/assurdità"} +{"ordine cluster":"","cluster tipologia":"stato riflessivo","ordine tipologia":"7","tipologia":"mistero/assurdità"} , -{"ordine cluster":"","cluster tipologia":"","ordine tipologia":"6","tipologia":"ipotesi"} +{"ordine cluster":"","cluster tipologia":"stato riflessivo","ordine tipologia":"6","tipologia":"ipotesi"} , {"ordine cluster":"2","cluster tipologia":"stato sospeso","ordine tipologia":"5","tipologia":"pausa/sospensione"} , -{"ordine cluster":"","cluster tipologia":"","ordine tipologia":"4","tipologia":"attesa"} +{"ordine cluster":"","cluster tipologia":"stato sospeso","ordine tipologia":"4","tipologia":"attesa"} , {"ordine cluster":"1","cluster tipologia":"situazione","ordine tipologia":"3","tipologia":"cambiamento"} , -{"ordine cluster":"","cluster tipologia":"","ordine tipologia":"2","tipologia":"città magica"} +{"ordine cluster":"","cluster tipologia":"situazione","ordine tipologia":"2","tipologia":"città magica"} , -{"ordine cluster":"","cluster tipologia":"","ordine tipologia":"1","tipologia":"situazione"} +{"ordine cluster":"","cluster tipologia":"situazione","ordine tipologia":"1","tipologia":"situazione"} , {"ordine cluster":"-1","cluster tipologia":"racconto incastonato","ordine tipologia":"-1","tipologia":"racconto incastonato"} ,