main
reng 4 weeks ago
parent fc0bf216e1
commit 4093e6b056
  1. 2
      v2/app/src/components/graph.jsx
  2. 89
      v2/app/src/components/point.jsx
  3. 4
      v2/app/src/utils/draw.js
  4. 27
      v2/app/src/utils/parsing.js
  5. 2
      v2/scrapper/main.js

@ -68,7 +68,7 @@ export default function Graph({results}){
console.log("3D Points:", newPoints); console.log("3D Points:", newPoints);
setPoints(newPoints); setPoints(newPoints);
generateLinesFromPoints(newPoints).then(newLines=>{ generateLinesFromPoints(newPoints, 10).then(newLines=>{
console.log("Lines:", newLines); console.log("Lines:", newLines);
setLines(newLines); setLines(newLines);
}); });

@ -3,6 +3,7 @@ import { useEffect, useRef, useState } from 'react';
import * as THREE from 'three'; import * as THREE from 'three';
import { useFrame, useThree } from '@react-three/fiber'; import { useFrame, useThree } from '@react-three/fiber';
import gsap from 'gsap'; import gsap from 'gsap';
import { normalize } from 'umap-js/dist/matrix';
export const PointScale=200; export const PointScale=200;
@ -10,11 +11,12 @@ const PointSize=1;
const PointDuration=3; const PointDuration=3;
const KeywordOffset=0; const KeywordOffset=0;
const SceneDuration=10; //
const DEPTH_CONFIG = { const DEPTH_CONFIG = {
NEAR: 600, // NEAR: 1200, //
FAR: 1000, // FAR: 1500, //
FADE_START: 400 FADE_START: 800
}; };
@ -35,13 +37,14 @@ export default function Point({point, index, totalPoints, result, showContent, s
const meshRef=useRef(); const meshRef=useRef();
const refText=useRef(); const refText=useRef();
const { camera } = useThree(); const { camera } = useThree();
const refAnimation=useRef({t:0});
function getRandomText(text){ function getRandomText(text){
if(contentType==PointContentType.Blur) return text.slice(0, textToShow) + '...'; if(contentType==PointContentType.Blur) return text.slice(0, textToShow) + '...';
const length=text.length; const length=text.length;
const characters='░▒▓█'; const characters='*@#$%^&';
let result=''; let result='';
for(let i=0; i<length; i++){ for(let i=0; i<length; i++){
if(Math.random() < 0.8) if(Math.random() < 0.8)
@ -101,7 +104,7 @@ export default function Point({point, index, totalPoints, result, showContent, s
// HTML // HTML
if (refText.current) { if (refText.current) {
refText.current.style.opacity = alpha; refText.current.style.opacity *= alpha;
// //
const finalHtmlScale = depthScaleFactor * (currentMeshScale / PointSize); const finalHtmlScale = depthScaleFactor * (currentMeshScale / PointSize);
refText.current.style.transform = `translate(-50%, -50%) scale(${finalHtmlScale})`; refText.current.style.transform = `translate(-50%, -50%) scale(${finalHtmlScale})`;
@ -114,51 +117,54 @@ export default function Point({point, index, totalPoints, result, showContent, s
refText.current.style.display = alpha <= 0.01 ? 'none' : 'block'; refText.current.style.display = alpha <= 0.01 ? 'none' : 'block';
} }
}); });
function setTextStyle(t){
if(refText.current){
refText.current.style.opacity=t;
refText.current.style.transform=`translate(-50%, -50%) scale(${t})`;
}
}
useEffect(()=>{ useEffect(()=>{
return; // return;
if(!payload) return; if(!payload) return;
const lifeTime=payload.number/payload.total; const lifeTime=payload.normalized_time || 0;
console.log("Point lifeTime:", lifeTime); // console.log("Point lifeTime:", lifeTime);
// animate point size based on lifeTime // animate point size based on lifeTime
const targetSize=1; const targetSize=1;
const initialSize=targetSize;
const timeline=gsap.timeline({ repeat:-1}); const timeline=gsap.timeline({ repeat:-1});
timeline.set(meshRef.current.scale, { timeline.fromTo(refAnimation.current,{t:0}, {
x: initialSize, y: initialSize, z: initialSize, t:0,
}); onUpdate: ()=>{
timeline.set(refText.current, { setTextStyle(refAnimation.current.t);
scale: initialSize,
opacity: 0,
});
timeline.fromTo(meshRef.current.scale, {
x: initialSize, y: initialSize, z: initialSize,
}, {
x: PointSize, y: PointSize, z: PointSize,
duration:PointDuration,
delay: lifeTime *PointDuration,
ease: "power3.in",
onUpdate:()=>{
if(refText.current){
refText.current.style.transform=`scale(${meshRef.current.scale.x / PointSize})`;
refText.current.style.opacity=`${meshRef.current.scale.x / PointSize}`;
}
} }
}); }, lifeTime * SceneDuration);
timeline.to(meshRef.current.scale, { timeline.to(refAnimation.current,{
x: targetSize, y: targetSize, z: targetSize, t: 1,
duration:PointDuration, duration: PointDuration,
ease: "power3.out", ease: 'power1.inOut',
onUpdate:()=>{ onComplete: ()=>{
if(refText.current){ console.log("Show text for point:", index);
refText.current.style.transform=`scale(${meshRef.current.scale.x / PointSize})`; },
refText.current.style.opacity=`${meshRef.current.scale.x / PointSize}`; onUpdate: ()=>{
} setTextStyle(refAnimation.current.t);
}
}); // lifeTime
timeline.to(refAnimation.current,{
t: 0,
duration: PointDuration,
onComplete: ()=>{
console.log("Hide text for point:", index);
},
onUpdate: ()=>{
setTextStyle(refAnimation.current.t);
} }
}); });
// timeline.to(refAnimation.current,{t:0}, {
// duration: SceneDuration - (lifeTime * SceneDuration + PointDuration * 2),
// }); //
timeline.play(); timeline.play();
@ -177,6 +183,7 @@ export default function Point({point, index, totalPoints, result, showContent, s
total: result.payload.total || '1', total: result.payload.total || '1',
content: JSON.parse(result.payload.metadata).text || '', content: JSON.parse(result.payload.metadata).text || '',
teaser: result.payload.teaser || '', teaser: result.payload.teaser || '',
normalized_time: result.payload.normalized_time || 0,
}); });
},[point]); },[point]);
@ -191,13 +198,13 @@ export default function Point({point, index, totalPoints, result, showContent, s
emissiveIntensity={0.5} emissiveIntensity={0.5}
/> />
<Html> <Html>
<div ref={refText} className='text-white p-2 w-[20vw] select-none translate-x-[-50%] translate-y-[-50%] text-center opacity-0'> <div ref={refText} className='text-white p-2 select-none text-center opacity-0'>
{showKeyword && <div className='flex flex-row justify-center flex-wrap text-[1rem]'> {showKeyword && <div className='flex flex-row justify-center flex-wrap text-[1rem]'>
{payload?.keywords.map((el, index)=><span key={index} className='px-2' style={{transform: `translate(${Math.random() * KeywordOffset}px,${Math.random() * KeywordOffset}px)`}}>{el}</span>)} {payload?.keywords.map((el, index)=><span key={index} className='px-2' style={{transform: `translate(${Math.random() * KeywordOffset}px,${Math.random() * KeywordOffset}px)`}}>{el}</span>)}
</div>} </div>}
{showContent && <div {showContent && <div
className='text-[2rem] whitespace-pre-wrap text-white rounded p-2 text-left' className='text-[2rem] text-white rounded p-2 text-center w-[100vw]'
style={{width: `${Math.random()*(payload?.content.length/5 || 10) + 20}vw`}} // style={{width: `${Math.random()*(payload?.content.length/5 || 10) + 20}vw`}}
dangerouslySetInnerHTML={{__html: getContent()}} dangerouslySetInnerHTML={{__html: getContent()}}
></div>} ></div>}
</div> </div>

@ -18,7 +18,7 @@ export async function get3dCoordinates(rawPoints) {
} }
export async function generateLinesFromPoints(points){ export async function generateLinesFromPoints(points, maxDistance=300) {
if(!Array.isArray(points) || points.length<2){ if(!Array.isArray(points) || points.length<2){
return []; return [];
} }
@ -34,7 +34,7 @@ export async function generateLinesFromPoints(points){
Math.pow(end[1]-start[1], 2) + Math.pow(end[1]-start[1], 2) +
Math.pow(end[2]-start[2], 2) Math.pow(end[2]-start[2], 2)
); );
if(distance > 20) continue; // skip if too far apart if(distance > maxDistance) continue; // skip if too far apart
lines.push([start.map(coord => coord * PointScale), end.map(coord => coord * PointScale)]); lines.push([start.map(coord => coord * PointScale), end.map(coord => coord * PointScale)]);
} }

@ -130,7 +130,10 @@ export async function searchByTheme(themeId, limit){
const data = await res.json(); const data = await res.json();
console.log(data); console.log(data);
return await searchQdrant(data.result.vector, limit); const allResults=await searchQdrant(data.result.vector, limit);
normalizeResultTime(allResults);
return allResults;
} }
export async function searchByKeywords(keywordIds, limit){ export async function searchByKeywords(keywordIds, limit){
@ -156,6 +159,8 @@ export async function searchByKeywords(keywordIds, limit){
allResults.push(...results); allResults.push(...results);
} }
normalizeResultTime(allResults);
return allResults; return allResults;
} }
@ -185,3 +190,23 @@ async function searchQdrant(query_embeddings, limit=10){
return data.result; return data.result;
} }
function normalizeResultTime(results){
let min, max;
results.forEach(item=>{
const time = JSON.parse(item.payload.metadata).published_on;
if(!min || time<min) min=time;
if(!max || time>max) max=time;
});
console.log("Time range:", min, max);
results.forEach(item=>{
const time = JSON.parse(item.payload.metadata).published_on;
item.payload.normalized_time = (time - min) / (max - min);
});
return results;
}

@ -53,7 +53,7 @@ const Keywords=[
const Version="v2-1"; const Version="v2-1";
const DEBUG_MODE=false; const DEBUG_MODE=false;
const SCRAP_TYPE='TAG'; // 'KEYWORD' or 'TAG' const SCRAP_TYPE='KEYWORD'; // 'KEYWORD' or 'TAG'
async function step1(){ async function step1(){
// const chooseKeywords=['人生 妥協','邊界 被侵犯','卡住 職涯','尷尬 年齡','厭世 創作']; // const chooseKeywords=['人生 妥協','邊界 被侵犯','卡住 職涯','尷尬 年齡','厭世 創作'];

Loading…
Cancel
Save