diff --git a/federjs/Feder.ts b/federjs/Feder.ts index a777f41..4f3d1a3 100644 --- a/federjs/Feder.ts +++ b/federjs/Feder.ts @@ -2,6 +2,7 @@ import * as d3 from 'd3'; import { EActionType, EIndexType, + EMediaType, ESourceType, EViewType, TId, @@ -37,8 +38,15 @@ export class Feder { const { viewType = EViewType.default } = viewParams; this.viewType = viewType; + // Compatible with feder_v0.x if (!viewParams.mediaContent && !!viewParams.mediaCallback) viewParams.mediaContent = viewParams.mediaCallback; + if (viewParams.mediaType === 'img') viewParams.mediaType = EMediaType.image; + if (!!viewParams.projectSeed && !viewParams?.projectParams?.projectSeed) + viewParams.projectParams = Object.assign({}, viewParams.projectParams, { + projectSeed: viewParams.projectSeed, + }); + this.viewParams = viewParams; this.searchParams = {}; diff --git a/federjs/FederIndex/id2VectorHandler/index.ts b/federjs/FederIndex/id2VectorHandler/index.ts index f5cbbcc..6238fed 100644 --- a/federjs/FederIndex/id2VectorHandler/index.ts +++ b/federjs/FederIndex/id2VectorHandler/index.ts @@ -26,7 +26,6 @@ const getId2VectorMap = { export default function id2VectorHandler(index: TIndexStructure) { const indexType = index.indexType; - const getId2Vector = getId2VectorMap[indexType]; const id2Vector = getId2Vector(index); return id2Vector; diff --git a/federjs/FederLayout/visDataHandler/ivfflat/index.ts b/federjs/FederLayout/visDataHandler/ivfflat/index.ts index c71ab0d..6b2eceb 100644 --- a/federjs/FederLayout/visDataHandler/ivfflat/index.ts +++ b/federjs/FederLayout/visDataHandler/ivfflat/index.ts @@ -33,6 +33,7 @@ const layoutParamsIvfflatDefault = { nonTopkNodeR: 3, minVoronoiRadius: 5, projectPadding: [20, 30, 20, 30], + staticPanelWidth: 240, }; export default class FederLayoutIvfflat implements TFederLayoutHandler { overviewLayoutParams: TLayoutParamsIvfflat = {}; diff --git a/federjs/FederLayout/visDataHandler/ivfflat/search/finePolar.ts b/federjs/FederLayout/visDataHandler/ivfflat/search/finePolar.ts index 6b0dc46..e141de8 100644 --- a/federjs/FederLayout/visDataHandler/ivfflat/search/finePolar.ts +++ b/federjs/FederLayout/visDataHandler/ivfflat/search/finePolar.ts @@ -22,7 +22,12 @@ export const ivfflatSearchViewLayoutFinePolar = ({ polarMaxR: number; }) => new Promise((resolve) => { - const { numForceIterations, nonTopkNodeR, canvasScale, polarRadiusUpperBound } = layoutParams; + const { + numForceIterations, + nonTopkNodeR, + canvasScale, + polarRadiusUpperBound, + } = layoutParams; const clusterId2cluster = {} as { [clusterId: TId]: TVisDataIvfflatSearchViewCluster; }; @@ -41,8 +46,13 @@ export const ivfflatSearchViewLayoutFinePolar = ({ searchViewNodes.sort((a, b) => a.distance - b.distance); // distance scale - const minDis = getPercentile(searchViewNodes, 'distance', 0); - const maxDis = getPercentile(searchViewNodes, 'distance', polarRadiusUpperBound); + const minDis = + searchViewNodes[Math.min(searchViewNodes.length - 1, 1)].distance; + const maxDis = getPercentile( + searchViewNodes, + 'distance', + polarRadiusUpperBound + ); const r = d3 .scaleLinear() .domain([minDis, maxDis]) diff --git a/federjs/FederLayout/visDataHandler/ivfflat/search/fineProject.ts b/federjs/FederLayout/visDataHandler/ivfflat/search/fineProject.ts index 37820d6..1be4f04 100644 --- a/federjs/FederLayout/visDataHandler/ivfflat/search/fineProject.ts +++ b/federjs/FederLayout/visDataHandler/ivfflat/search/fineProject.ts @@ -22,6 +22,7 @@ export const ivfflatSearchViewLayoutFineProject = ({ new Promise((resolve) => { const { projectPadding, + staticPanelWidth, width, height, canvasScale, @@ -44,13 +45,20 @@ export const ivfflatSearchViewLayoutFineProject = ({ node.projection = searchviewNodesProjection[i]; }); + const xRange = targetNode.isLeft_coarseLevel + ? [ + projectPadding[3] * canvasScale, + (width - projectPadding[1]) * canvasScale - + staticPanelWidth * canvasScale, + ] + : [ + projectPadding[3] * canvasScale + staticPanelWidth * canvasScale, + (width - projectPadding[1]) * canvasScale, + ]; const x = d3 .scaleLinear() .domain(d3.extent(searchViewNodes, (node) => node.projection[0])) - .range([ - projectPadding[3] * canvasScale, - (width - projectPadding[1]) * canvasScale, - ]); + .range(xRange); const y = d3 .scaleLinear() .domain(d3.extent(searchViewNodes, (node) => node.projection[1])) diff --git a/federjs/FederView/hnswView/defaultViewParamsHnsw.ts b/federjs/FederView/hnswView/defaultViewParamsHnsw.ts index cc8dd10..0d5bb13 100644 --- a/federjs/FederView/hnswView/defaultViewParamsHnsw.ts +++ b/federjs/FederView/hnswView/defaultViewParamsHnsw.ts @@ -62,5 +62,11 @@ export const defaultViewParamsHnsw = { tipLineWidth: 2, mediaContentCount: 9, + + staticPanelWidth: 240, + hoveredPanelWidth: 250, + clickedPanelWidth: 210, + + getVectorById: async () => [], } as TViewParamsHnsw; export default defaultViewParamsHnsw; diff --git a/federjs/FederView/hnswView/infoPanelStyles.ts b/federjs/FederView/hnswView/infoPanelStyles.ts index 38d7385..41372a8 100644 --- a/federjs/FederView/hnswView/infoPanelStyles.ts +++ b/federjs/FederView/hnswView/infoPanelStyles.ts @@ -1,10 +1,17 @@ import { hexWithOpacity } from 'FederView/renderUtils2D'; -export const staticPanelStyles = ({ width, height, padding }) => ({ +export const staticPanelStyles = ({ + height, + staticPanelWidth, +}: { + height: number; + staticPanelWidth?: number; +}) => ({ position: 'absolute', left: '16px', top: '16px', - width: padding ? `${padding[3] + 10}px` : `${width * 0.3}px`, + + width: `${staticPanelWidth}px`, 'max-height': `${height - 110}px`, overflow: 'auto', borderColor: '#FFFFFF', @@ -12,20 +19,30 @@ export const staticPanelStyles = ({ width, height, padding }) => ({ // pointerEvents: 'none', }); -export const clickedPanelStyles = ({ width, height, padding }) => ({ +export const clickedPanelStyles = ({ + height, + clickedPanelWidth, +}: { + height: number; + clickedPanelWidth?: number; +}) => ({ position: 'absolute', right: '16px', top: '16px', - width: padding ? `${padding[1] - 10}px` : `${width * 0.3}px`, + width: `${clickedPanelWidth}px`, 'max-height': `${height - 60}px`, overflow: 'auto', borderColor: '#FFFFFF', backgroundColor: hexWithOpacity('#000000', 0.6), }); -export const hoveredPanelStyles = ({ width }) => ({ +export const hoveredPanelStyles = ({ + hoveredPanelWidth, +}: { + hoveredPanelWidth: number; +}) => ({ position: 'absolute', - width: `${width * 0.3}px`, + width: `${hoveredPanelWidth}px`, paddingLeft: '6px', left: 0, top: 0, diff --git a/federjs/FederView/ivfflatView/IvfflatSearchView/updateHoveredPanelNodeView.ts b/federjs/FederView/ivfflatView/IvfflatSearchView/updateHoveredPanelNodeView.ts index 246b439..136664f 100644 --- a/federjs/FederView/ivfflatView/IvfflatSearchView/updateHoveredPanelNodeView.ts +++ b/federjs/FederView/ivfflatView/IvfflatSearchView/updateHoveredPanelNodeView.ts @@ -21,6 +21,7 @@ export default async function updateHoveredPanelNodeView( hasBorder: false, content: [ { title: `Row No. ${node.id}` }, + { text: `distance: ${node.distance.toFixed(3)}` }, { text: `belong to cluster-${node.clusterId}`, }, diff --git a/federjs/FederView/ivfflatView/defaultViewParamsIvfflat.ts b/federjs/FederView/ivfflatView/defaultViewParamsIvfflat.ts index ee62fb9..5641113 100644 --- a/federjs/FederView/ivfflatView/defaultViewParamsIvfflat.ts +++ b/federjs/FederView/ivfflatView/defaultViewParamsIvfflat.ts @@ -42,6 +42,11 @@ export const defaltViewParamsIvfflat = { transitionNodesMoveTime: 800, mediaContentCount: 9, + staticPanelWidth: 240, + hoveredPanelWidth: 250, + clickedPanelWidth: 210, + + getVectorById: async () => [] } as TViewParamsIvfflat; export default defaltViewParamsIvfflat; diff --git a/federjs/FederView/ivfflatView/infoPanelStyles.ts b/federjs/FederView/ivfflatView/infoPanelStyles.ts index 59e5f0e..d27cd89 100644 --- a/federjs/FederView/ivfflatView/infoPanelStyles.ts +++ b/federjs/FederView/ivfflatView/infoPanelStyles.ts @@ -1,18 +1,16 @@ import { hexWithOpacity } from 'FederView/renderUtils2D'; export const staticPanelStyles = ({ - width, height, - padding, + staticPanelWidth, }: { - width: number; height: number; - padding?: number[]; + staticPanelWidth?: number; }) => ({ position: 'absolute', left: '16px', top: '16px', - width: padding ? `${padding[3] + 10}px` : `${width * 0.3}px`, + width: `${staticPanelWidth}px`, 'max-height': `${height - 110}px`, overflow: 'auto', borderColor: '#FFFFFF', @@ -21,27 +19,25 @@ export const staticPanelStyles = ({ }); export const clickedPanelStyles = ({ - width, height, - padding, + clickedPanelWidth, }: { - width: number; height: number; - padding?: number[]; + clickedPanelWidth?: number; }) => ({ position: 'absolute', right: '16px', top: '16px', - width: padding ? `${padding[1] - 10}px` : `${width * 0.3}px`, + width: `${clickedPanelWidth}px`, 'max-height': `${height - 60}px`, overflow: 'auto', borderColor: '#FFFFFF', backgroundColor: hexWithOpacity('#000000', 0.6), }); -export const hoveredPanelStyles = ({ width }) => ({ +export const hoveredPanelStyles = ({ hoveredPanelWidth }: {hoveredPanelWidth: number}) => ({ position: 'absolute', - width: `${width * 0.3}px`, + width: `${hoveredPanelWidth}px`, paddingLeft: '6px', left: 0, top: 0, diff --git a/federjs/Types/visData/visDataHnswDefault.ts b/federjs/Types/visData/visDataHnswDefault.ts index 862e5b5..3d5d7e3 100644 --- a/federjs/Types/visData/visDataHnswDefault.ts +++ b/federjs/Types/visData/visDataHnswDefault.ts @@ -142,6 +142,10 @@ export interface TViewParamsHnsw extends TViewParams { tipLineAngle?: number; tipLineColor?: string; tipLineWidth?: number; + + staticPanelWidth?: number; + hoveredPanelWidth: number; + clickedPanelWidth: number; } export interface TLayoutParamsHnsw { diff --git a/federjs/Types/visData/visDataIvfflat.ts b/federjs/Types/visData/visDataIvfflat.ts index afdfbae..e2da65f 100644 --- a/federjs/Types/visData/visDataIvfflat.ts +++ b/federjs/Types/visData/visDataIvfflat.ts @@ -85,6 +85,7 @@ export interface TLayoutParamsIvfflat { polarRadiusUpperBound?: number; projectPadding?: [number, number, number, number]; + staticPanelWidth?: number; } export interface TViewParamsIvfflat extends TViewParams { @@ -127,4 +128,8 @@ export interface TViewParamsIvfflat extends TViewParams { transitionReplaceTime: number; transitionNodesEnterTime: number; transitionNodesMoveTime: number; + + staticPanelWidth?: number; + hoveredPanelWidth: number; + clickedPanelWidth: number; } diff --git a/federjs_old/Feder.js b/federjs_old/Feder.js deleted file mode 100644 index 36fb76a..0000000 --- a/federjs_old/Feder.js +++ /dev/null @@ -1,142 +0,0 @@ -import FederCore from './FederCore'; -import FederView from './FederView'; -import { FEDER_CORE_REQUEST } from 'Types'; - -export default class Feder { - constructor({ - coreUrl = null, - filePath = '', - source = '', - domSelector = null, - viewParams = {}, - }) { - this.federView = new FederView({ domSelector, viewParams }); - this.viewParams = viewParams; - if (!coreUrl) { - this.initCoreAndViewPromise = fetch(filePath, { mode: 'cors' }) - .then((res) => res.arrayBuffer()) - .then((data) => { - const core = new FederCore({ data, source, viewParams }); - this.core = core; - const indexType = core.indexType; - const indexMeta = core.indexMeta; - const getVectorById = (id) => - id in core.id2vector ? core.id2vector[id] : null; - this.core.getVectorById = getVectorById; - this.federView.initView({ - indexType, - indexMeta, - getVectorById, - }); - }); - } else { - const getUrl = (path) => `${coreUrl}/${path}?`; - - const requestData = (path, params = {}) => - fetch(getUrl(path) + new URLSearchParams(params), { - mode: 'cors', - }) - .then((res) => res.json()) - .then((res) => { - if (res.message === 'succeed') return res.data; - else throw new Error(res); - }); - - this.initCoreAndViewPromise = new Promise(async (resolve) => { - const indexType = await requestData(FEDER_CORE_REQUEST.get_index_type); - const indexMeta = await requestData(FEDER_CORE_REQUEST.get_index_meta); - const getVectorById = (id) => - requestData(FEDER_CORE_REQUEST.get_vector_by_id, { id }); - this.core = { - indexType, - indexMeta, - getVectorById, - getTestIdAndVec: () => - requestData(FEDER_CORE_REQUEST.get_test_id_and_vector), - search: (target) => - requestData(FEDER_CORE_REQUEST.search, { target }), - setSearchParams: (params) => - requestData(FEDER_CORE_REQUEST.set_search_params, params), - }; - this.federView.initView({ indexType, indexMeta, getVectorById }); - resolve(); - }); - } - - this.setSearchParamsPromise = null; - } - - overview() { - return this.federView.overview(this.initCoreAndViewPromise); - } - search(target = null, targetMediaUrl = null) { - if (target) { - const searchResPromise = Promise.all([ - this.initCoreAndViewPromise, - this.setSearchParamsPromise, - ]).then(async () => { - const searchRes = await this.core.search(target); - console.log(searchRes); - this.searchRes = searchRes; - this.targetMediaUrl = targetMediaUrl; - return { searchRes, targetMediaUrl }; - }); - return this.federView.search({ searchResPromise }); - } else { - if (!this.searchRes) { - console.error('No target'); - return; - } - const searchRes = this.searchRes; - const targetMediaUrl = this.targetMediaUrl; - return this.federView.search({ searchRes, targetMediaUrl }); - } - } - searchById(testId) { - const searchResPromise = this.initCoreAndViewPromise.then(async () => { - const testVec = await this.core.getVectorById(testId); - const targetMediaUrl = - this.viewParams && this.viewParams.mediaCallback - ? this.viewParams.mediaCallback(testId) - : null; - const searchRes = await this.core.search(testVec); - console.log(searchRes); - this.searchRes = searchRes; - return { searchRes, targetMediaUrl }; - }); - return this.federView.search({ searchResPromise }); - } - searchRandTestVec() { - const searchResPromise = new Promise(async (resolve) => { - this.initCoreAndViewPromise && (await this.initCoreAndViewPromise); - let { testId, testVec } = await this.core.getTestIdAndVec(); - while (isNaN(testId)) { - [testId, testVec] = await this.core.getTestIdAndVec(); - } - console.log('random test vector:', testId, testVec); - const targetMediaUrl = - this.viewParams && this.viewParams.mediaCallback - ? this.viewParams.mediaCallback(testId) - : null; - const searchRes = await this.core.search(testVec); - console.log(searchRes); - this.searchRes = searchRes; - resolve({ searchRes, targetMediaUrl }); - }); - - return this.federView.search({ searchResPromise }); - } - - setSearchParams(params) { - this.setSearchParamsPromise = new Promise(async (resolve) => { - this.initCoreAndViewPromise && (await this.initCoreAndViewPromise); - if (!this.core) { - console.error('No feder-core'); - } else { - await this.core.setSearchParams(params); - } - resolve(); - }); - return this; - } -} diff --git a/federjs_old/FederCore/index.js b/federjs_old/FederCore/index.js deleted file mode 100644 index 0fcd8ca..0000000 --- a/federjs_old/FederCore/index.js +++ /dev/null @@ -1,89 +0,0 @@ -import getIndexParser from './parser'; -import getMetaHandler from './metaHandler'; -import getIndexSearchHandler from './searchHandler'; -import getProjectorHandler from './projector'; -import seedrandom from 'seedrandom'; -import { INDEX_TYPE } from 'Types'; -export default class FederCore { - constructor({ - data, // arraybuffer - source, - viewParams, - }) { - try { - this.viewParams = viewParams; - const index = this.parserIndex(data, source); - this.index = index; - this.indexType = index.indexType; - - const { indexMeta, id2vector } = this.extractMeta(index, viewParams); - this.indexMeta = indexMeta; - this.id2vector = id2vector; - - this.indexSearchHandler = getIndexSearchHandler(index.indexType); - } catch (e) { - console.log(e); - } - } - parserIndex(data, source) { - const indexParser = getIndexParser(source); - const index = indexParser(data); - return index; - } - extractMeta(index, viewParams = {}) { - const metaHandler = getMetaHandler(index.indexType); - const meta = metaHandler(index, viewParams); - return meta; - } - getTestIdAndVec() { - const ids = Object.keys(this.id2vector); - const r = Math.floor(Math.random() * ids.length); - const testId = ids[r]; - const testVec = this.id2vector[testId]; - return [testId, testVec]; - } - - setSearchParams(params) { - const newSearchParams = Object.assign({}, this.searchParams, params); - this.searchParams = newSearchParams; - } - search(target) { - const searchRes = this.indexSearchHandler({ - index: this.index, - params: this.searchParams, - target, - }); - - if (this.index.indexType === INDEX_TYPE.ivf_flat) { - const { - fineSearchWithProjection = true, - projectMethod = 'umap', - projectSeed = null, - projectParams = {}, - } = this.viewParams; - if (fineSearchWithProjection) { - const ids = searchRes.fine.map((item) => item.id); - const params = projectParams; - if (!!projectSeed) { - params.random = seedrandom(projectSeed); - } - this.initProjector({ - method: projectMethod, - params, - }); - const projections = this.projectByIds(ids); - searchRes.fine.map((item, i) => (item.projection = projections[i])); - } - } - - return searchRes; - } - - initProjector({ method, params = {} }) { - this.project = getProjectorHandler({ method, params }); - } - projectByIds(ids) { - const vectors = ids.map((id) => this.id2vector[id]); - return this.project(vectors); - } -} diff --git a/federjs_old/FederCore/metaHandler/hnswMeta/hnswOverviewData.js b/federjs_old/FederCore/metaHandler/hnswMeta/hnswOverviewData.js deleted file mode 100644 index 4cbb0c5..0000000 --- a/federjs_old/FederCore/metaHandler/hnswMeta/hnswOverviewData.js +++ /dev/null @@ -1,54 +0,0 @@ -const getHnswlibHNSWOverviewData = ({ index, overviewLevelCount = 3 }) => { - const { maxLevel, linkLists_levels, labels, enterPoint } = index; - const highlevel = Math.min(maxLevel, overviewLevelCount); - const lowlevel = maxLevel - highlevel; - - const highLevelNodes = linkLists_levels - .map((linkLists_levels_item, internalId) => - linkLists_levels_item.length > lowlevel - ? { - internalId, - id: labels[internalId], - linksLevels: linkLists_levels_item.slice( - lowlevel, - linkLists_levels_item.length - ), - path: [], - } - : null - ) - .filter((d) => d); - // console.log('highLevelNodes', highLevelNodes) - - const internalId2node = {}; - highLevelNodes.forEach((node) => { - internalId2node[node.internalId] = node; - }); - - let queue = [enterPoint]; - let start = 0; - internalId2node[enterPoint].path = []; - for (let level = highlevel - 1; level >= 0; level--) { - while (start < queue.length) { - const curNodeId = queue[start]; - const curNode = internalId2node[curNodeId]; - const candidateNodes = curNode.linksLevels[level]; - candidateNodes.forEach((candidateNodeId) => { - const candidateNode = internalId2node[candidateNodeId]; - if (candidateNode.path.length === 0 && candidateNodeId !== enterPoint) { - candidateNode.path = [...curNode.path, curNodeId]; - queue.push(candidateNodeId); - } - }); - start += 1; - } - queue = highLevelNodes - .filter((node) => node.linksLevels.length > level) - .map((node) => node.internalId); - start = 0; - } - - return highLevelNodes; -}; - -export default getHnswlibHNSWOverviewData; diff --git a/federjs_old/FederCore/metaHandler/hnswMeta/index.js b/federjs_old/FederCore/metaHandler/hnswMeta/index.js deleted file mode 100644 index 2b40d7b..0000000 --- a/federjs_old/FederCore/metaHandler/hnswMeta/index.js +++ /dev/null @@ -1,26 +0,0 @@ -import getHnswlibHNSWOverviewData from './hnswOverviewData'; -export const getHnswMeta = (index, { overviewLevelCount = 3 }) => { - const { labels, vectors } = index; - - const id2vector = {}; - labels.forEach((id, i) => { - id2vector[id] = vectors[i]; - }); - - const overviewNodes = getHnswlibHNSWOverviewData({ - index, - overviewLevelCount, - }); - const indexMeta = { - ntotal: index.ntotal, - M: index.M, - ef_construction: index.ef_construction, - overviewLevelCount, - levelCount: index.maxLevel + 1, - overviewNodes, - }; - - return { id2vector, indexMeta }; -}; - -export default getHnswMeta; diff --git a/federjs_old/FederCore/metaHandler/index.js b/federjs_old/FederCore/metaHandler/index.js deleted file mode 100644 index f7b6bfc..0000000 --- a/federjs_old/FederCore/metaHandler/index.js +++ /dev/null @@ -1,17 +0,0 @@ -import { INDEX_TYPE } from 'Types'; -import getHnswMeta from './hnswMeta'; -import getIvfflatMeta from './ivfflatMeta'; - - -const metaHandlerMap = { - [INDEX_TYPE.hnsw]: getHnswMeta, - [INDEX_TYPE.ivf_flat]: getIvfflatMeta, -}; - -export const getMetaHandler = (indexType) => { - if (indexType in metaHandlerMap) { - return metaHandlerMap[indexType]; - } else throw `No meta handler for [${indexType}]`; -}; - -export default getMetaHandler; diff --git a/federjs_old/FederCore/metaHandler/ivfflatMeta/index.js b/federjs_old/FederCore/metaHandler/ivfflatMeta/index.js deleted file mode 100644 index 0957e2c..0000000 --- a/federjs_old/FederCore/metaHandler/ivfflatMeta/index.js +++ /dev/null @@ -1,42 +0,0 @@ -import { getIvfListId } from 'Utils'; -import getProjectorHandler from 'FederCore/projector'; -import seedrandom from 'seedrandom'; - -export const getIvfflatMeta = (index, params) => { - const id2vector = {}; - const inv = index.invlists; - for (let list_no = 0; list_no < inv.nlist; list_no++) { - inv.data[list_no].ids.forEach((id, ofs) => { - id2vector[id] = inv.data[list_no].vectors[ofs]; - }); - } - index.childIndex.vectors.forEach( - (vector, i) => (id2vector[getIvfListId(i)] = vector) - ); - - const indexMeta = {}; - indexMeta.ntotal = index.ntotal; - indexMeta.nlist = index.nlist; - indexMeta.listIds = index.invlists.data.map((d) => d.ids); - indexMeta.listSizes = index.invlists.data.map((d) => d.ids.length); - - const { - coarseSearchWithProjection = true, - projectMethod = 'umap', - projectSeed = null, - projectParams = {}, - } = params; - if (coarseSearchWithProjection) { - const params = Object.assign({}, projectParams); - if (!!projectSeed) { - params.random = seedrandom(projectSeed); - } - const project = getProjectorHandler({ method: projectMethod, params }); - const vectors = index.childIndex.vectors; - indexMeta.listCentroidProjections = project(vectors); - } - - return { id2vector, indexMeta }; -}; - -export default getIvfflatMeta; diff --git a/federjs_old/FederCore/parser/FileReader.js b/federjs_old/FederCore/parser/FileReader.js deleted file mode 100644 index 90f900f..0000000 --- a/federjs_old/FederCore/parser/FileReader.js +++ /dev/null @@ -1,72 +0,0 @@ -import { generateArray } from 'Utils'; - -export default class FileReader { - constructor(arrayBuffer) { - this.data = arrayBuffer; - this.dataview = new DataView(arrayBuffer); - - this.p = 0; - } - get isEmpty() { - return this.p >= this.data.byteLength; - } - readInt8() { - const int8 = this.dataview.getInt8(this.p, true); - this.p += 1; - return int8; - } - readUint8() { - const uint8 = this.dataview.getUint8(this.p, true); - this.p += 1; - return uint8; - } - readBool() { - const int8 = this.readInt8(); - return Boolean(int8); - } - readUint16() { - const uint16 = this.dataview.getUint16(this.p, true); - this.p += 2; - return uint16; - } - readInt32() { - const int32 = this.dataview.getInt32(this.p, true); - this.p += 4; - return int32; - } - readUint32() { - const uint32 = this.dataview.getUint32(this.p, true); - this.p += 4; - return uint32; - } - readUint64() { - const left = this.readUint32(); - const right = this.readUint32(); - const int64 = left + Math.pow(2, 32) * right; - if (!Number.isSafeInteger(int64)) - console.warn(int64, 'Exceeds MAX_SAFE_INTEGER. Precision may be lost'); - return int64; - } - readFloat64() { - const float64 = this.dataview.getFloat64(this.p, true); - this.p += 8; - return float64; - } - readDouble() { - return this.readFloat64(); - } - - readUint32Array(n) { - const res = generateArray(n).map((_) => this.readUint32()); - return res; - } - readFloat32Array(n) { - const res = new Float32Array(this.data.slice(this.p, this.p + n * 4)); - this.p += n * 4; - return res; - } - readUint64Array(n) { - const res = generateArray(n).map((_) => this.readUint64()); - return res; - } -} diff --git a/federjs_old/FederCore/parser/faissIndexParser/FaissFileReader.js b/federjs_old/FederCore/parser/faissIndexParser/FaissFileReader.js deleted file mode 100644 index 5c3a73b..0000000 --- a/federjs_old/FederCore/parser/faissIndexParser/FaissFileReader.js +++ /dev/null @@ -1,17 +0,0 @@ -import FileReader from '../FileReader.js'; -import { uint8toChars, generateArray } from 'Utils'; - -export default class FaissFileReader extends FileReader { - constructor(arrayBuffer) { - super(arrayBuffer); - } - readH() { - const uint8Array = generateArray(4).map((_) => this.readUint8()); - const h = uint8toChars(uint8Array); - return h; - } - readDummy() { - const dummy = this.readUint64(); - return dummy; - } -} diff --git a/federjs_old/FederCore/parser/faissIndexParser/index.js b/federjs_old/FederCore/parser/faissIndexParser/index.js deleted file mode 100644 index 3b810d2..0000000 --- a/federjs_old/FederCore/parser/faissIndexParser/index.js +++ /dev/null @@ -1,51 +0,0 @@ -import FaissFileReader from './FaissFileReader.js'; -import readInvertedLists from './readInvertedLists.js'; -import readDirectMap from './readDirectMap.js'; -import readIndexHeader from './readIndexHeader.js'; - -import { generateArray } from 'Utils'; -import { INDEX_TYPE, IndexHeader } from 'Types'; - -const readIvfHeader = (reader, index) => { - readIndexHeader(reader, index); - - index.nlist = reader.readUint64(); - index.nprobe = reader.readUint64(); - - index.childIndex = readIndex(reader); - - readDirectMap(reader, index); -}; - -const readXbVectors = (reader, index) => { - index.codeSize = reader.readUint64(); - - index.vectors = generateArray(index.ntotal).map((_) => - reader.readFloat32Array(index.d) - ); -}; - -const readIndex = (reader) => { - const index = {}; - index.h = reader.readH(); - if (index.h === IndexHeader.IVFFlat) { - index.indexType = INDEX_TYPE.ivf_flat; - readIvfHeader(reader, index); - readInvertedLists(reader, index); - } else if (index.h === IndexHeader.FlatIR || index.h === IndexHeader.FlatL2) { - index.indexType = INDEX_TYPE.flat; - readIndexHeader(reader, index); - readXbVectors(reader, index); - } else { - console.warn('[index type] not supported -', index.h); - } - return index; -}; - -const faissIndexParser = (arraybuffer) => { - const faissFileReader = new FaissFileReader(arraybuffer); - const index = readIndex(faissFileReader); - return index; -}; - -export default faissIndexParser; diff --git a/federjs_old/FederCore/parser/faissIndexParser/readDirectMap.js b/federjs_old/FederCore/parser/faissIndexParser/readDirectMap.js deleted file mode 100644 index 2927181..0000000 --- a/federjs_old/FederCore/parser/faissIndexParser/readDirectMap.js +++ /dev/null @@ -1,24 +0,0 @@ -import { DirectMapType } from 'Types'; - -const checkDmType = (dmType) => { - if (dmType !== DirectMapType.NoMap) { - console.warn('[directmap_type] only support NoMap.'); - } -}; - -const checkDmSize = (dmSize) => { - if (dmSize !== 0) { - console.warn('[directmap_size] should be 0.'); - } -}; - -const readDirectMap = (reader, index) => { - const directMap = {}; - directMap.dmType = reader.readUint8(); - checkDmType(directMap.dmType); - directMap.size = reader.readUint64(); - checkDmSize(directMap.size); - index.directMap = directMap; -}; - -export default readDirectMap; diff --git a/federjs_old/FederCore/parser/faissIndexParser/readIndexHeader.js b/federjs_old/FederCore/parser/faissIndexParser/readIndexHeader.js deleted file mode 100644 index 8c5102e..0000000 --- a/federjs_old/FederCore/parser/faissIndexParser/readIndexHeader.js +++ /dev/null @@ -1,39 +0,0 @@ -import { MetricType } from 'Types'; - -const checkMetricType = (metricType) => { - if ( - metricType !== MetricType.METRIC_L2 && - metricType !== MetricType.METRIC_INNER_PRODUCT - ) { - console.warn('[metric_type] only support l2 and inner_product.'); - } -}; - -const checkDummy = (dummy_1, dummy_2) => { - if (dummy_1 !== dummy_2) { - console.warn('[dummy] not equal.', dummy_1, dummy_2); - } -}; - -const checkIsTrained = (isTrained) => { - if (!isTrained) { - console.warn('[is_trained] should be trained.', isTrained); - } -}; - -const readIndexHeader = (reader, index) => { - index.d = reader.readUint32(); - index.ntotal = reader.readUint64(); - - const dummy_1 = reader.readDummy(); - const dummy_2 = reader.readDummy(); - checkDummy(dummy_1, dummy_2); - - index.isTrained = reader.readBool(); - checkIsTrained(index.isTrained); - - index.metricType = reader.readUint32(); - checkMetricType(index.metricType); -}; - -export default readIndexHeader; diff --git a/federjs_old/FederCore/parser/faissIndexParser/readInvertedLists.js b/federjs_old/FederCore/parser/faissIndexParser/readInvertedLists.js deleted file mode 100644 index d2b0ec1..0000000 --- a/federjs_old/FederCore/parser/faissIndexParser/readInvertedLists.js +++ /dev/null @@ -1,47 +0,0 @@ -import { generateArray } from 'Utils'; - -const checkInvH = (h) => { - if (h !== 'ilar') { - console.warn('[invlists h] not ilar.', h); - } -}; - -const checkInvListType = (listType) => { - if (listType !== 'full') { - console.warn('[inverted_lists list_type] only support full.', listType); - } -}; - -const readArrayInvLists = (reader, invlists) => { - invlists.listType = reader.readH(); - checkInvListType(invlists.listType); - - invlists.listSizesSize = reader.readUint64(); - invlists.listSizes = generateArray(invlists.listSizesSize).map((_) => - reader.readUint64() - ); - - const data = []; - generateArray(invlists.listSizesSize).forEach((_, i) => { - const vectors = generateArray(invlists.listSizes[i]).map((_) => - reader.readFloat32Array(invlists.codeSize / 4) - ); - const ids = reader.readUint64Array(invlists.listSizes[i]); - data.push({ ids, vectors }); - }); - invlists.data = data; -}; - -export const readInvertedLists = (reader, index) => { - const invlists = {}; - invlists.h = reader.readH(); - checkInvH(invlists.h); - invlists.nlist = reader.readUint64(); - invlists.codeSize = reader.readUint64(); - - readArrayInvLists(reader, invlists); - - index.invlists = invlists; -}; - -export default readInvertedLists; diff --git a/federjs_old/FederCore/parser/hnswlibIndexParser/HNSWlibFileReader.js b/federjs_old/FederCore/parser/hnswlibIndexParser/HNSWlibFileReader.js deleted file mode 100644 index 358a45c..0000000 --- a/federjs_old/FederCore/parser/hnswlibIndexParser/HNSWlibFileReader.js +++ /dev/null @@ -1,16 +0,0 @@ -import FileReader from '../FileReader.js'; - -export default class HNSWlibFileReader extends FileReader { - constructor(arrayBuffer) { - super(arrayBuffer); - } - readIsDeleted() { - return this.readUint8() - } - readIsReused() { - return this.readUint8() - } - readLevelOCount() { - return this.readUint16(); - } -} diff --git a/federjs_old/FederCore/parser/hnswlibIndexParser/index.js b/federjs_old/FederCore/parser/hnswlibIndexParser/index.js deleted file mode 100644 index 8e3d56b..0000000 --- a/federjs_old/FederCore/parser/hnswlibIndexParser/index.js +++ /dev/null @@ -1,98 +0,0 @@ -import HNSWlibFileReader from './HNSWlibFileReader.js'; -import { INDEX_TYPE } from 'Types'; -import { generateArray } from 'Utils'; - -export const hnswlibIndexParser = (arrayBuffer) => { - const reader = new HNSWlibFileReader(arrayBuffer); - const index = {}; - index.offsetLevel0_ = reader.readUint64(); - index.max_elements_ = reader.readUint64(); - index.cur_element_count = reader.readUint64(); - // index.ntotal = index.cur_element_count; // consistent with Faiss - index.size_data_per_element_ = reader.readUint64(); - index.label_offset_ = reader.readUint64(); - index.offsetData_ = reader.readUint64(); - index.dim = (index.size_data_per_element_ - index.offsetData_ - 8) / 4; - - index.maxlevel_ = reader.readUint32(); - index.enterpoint_node_ = reader.readUint32(); - index.maxM_ = reader.readUint64(); - index.maxM0_ = reader.readUint64(); - index.M = reader.readUint64(); - - index.mult_ = reader.readFloat64(); - index.ef_construction_ = reader.readUint64(); - - index.size_links_per_element_ = index.maxM_ * 4 + 4; - index.size_links_level0_ = index.maxM0_ * 4 + 4; - index.revSize_ = 1.0 / index.mult_; - index.ef_ = 10; - - read_data_level0_memory_(reader, index); - - const linkListSizes = []; - const linkLists_ = []; - for (let i = 0; i < index.cur_element_count; i++) { - const linkListSize = reader.readUint32(); - linkListSizes.push(linkListSize); - if (linkListSize === 0) { - linkLists_[i] = []; - } else { - const levelCount = linkListSize / 4 / (index.maxM_ + 1); - linkLists_[i] = generateArray(levelCount) - .map((_) => reader.readUint32Array(index.maxM_ + 1)) - .map((linkLists) => linkLists.slice(1, linkLists[0] + 1)); - // .filter((a) => a.length > 0); - } - } - index.linkListSizes = linkListSizes; - index.linkLists_ = linkLists_; - - console.assert( - reader.isEmpty, - 'HNSWlib Parser Failed. Not empty when the parser completes.' - ); - - return { - indexType: INDEX_TYPE.hnsw, - ntotal: index.cur_element_count, - vectors: index.vectors, - maxLevel: index.maxlevel_, - linkLists_level0_count: index.linkLists_level0_count, - linkLists_level_0: index.linkLists_level0, - linkLists_levels: index.linkLists_, - enterPoint: index.enterpoint_node_, - labels: index.externalLabel, - isDeleted: index.isDeleted, - numDeleted: index.num_deleted_, - M: index.M, - ef_construction: index.ef_construction_, - }; -}; - -const read_data_level0_memory_ = (reader, index) => { - // size_data = links_level0[M0 + 1] * 4 + vector[dim * 4] * 4 + label[1] * 8; - const isDeleted = []; - const linkLists_level0_count = []; - const linkLists_level0 = []; - const vectors = []; - const externalLabel = []; - for (let i = 0; i < index.cur_element_count; i++) { - const count = reader.readLevelOCount(); - linkLists_level0_count.push(count); - isDeleted.push(reader.readIsDeleted()); - reader.readIsReused(); // Unknown use. - linkLists_level0.push(reader.readUint32Array(index.maxM0_).slice(0, count)); - vectors.push(reader.readFloat32Array(index.dim)); - externalLabel.push(reader.readUint64()); - // console.log(isDeleted, linkLists_level0_count); - } - index.isDeleted = isDeleted; - index.num_deleted_ = isDeleted.reduce((acc, cur) => acc + cur, 0); - index.linkLists_level0_count = linkLists_level0_count; - index.linkLists_level0 = linkLists_level0; - index.vectors = vectors; - index.externalLabel = externalLabel; -}; - -export default hnswlibIndexParser; diff --git a/federjs_old/FederCore/parser/index.js b/federjs_old/FederCore/parser/index.js deleted file mode 100644 index 728d9b8..0000000 --- a/federjs_old/FederCore/parser/index.js +++ /dev/null @@ -1,16 +0,0 @@ -import { SOURCE_TYPE } from 'Types'; -import hnswlibIndexParser from './hnswlibIndexParser'; -import faissIndexParser from "./faissIndexParser"; - -const indexParserMap = { - [SOURCE_TYPE.hnswlib]: hnswlibIndexParser, - [SOURCE_TYPE.faiss]: faissIndexParser, -}; - -export const getIndexParser = (sourceType) => { - if (sourceType in indexParserMap) { - return indexParserMap[sourceType]; - } else throw `No index parser for [${sourceType}]`; -}; - -export default getIndexParser; diff --git a/federjs_old/FederCore/projector/index.js b/federjs_old/FederCore/projector/index.js deleted file mode 100644 index e22a2b6..0000000 --- a/federjs_old/FederCore/projector/index.js +++ /dev/null @@ -1,17 +0,0 @@ -import umapProject from './umap'; - -import { PROJECT_METHOD } from 'Types'; - -const projectorMap = { - [PROJECT_METHOD.umap]: umapProject, -}; - -export const getProjector = ({ method, params = {} }) => { - if (method in projectorMap) { - return projectorMap[method](params); - } else { - console.error(`No projector for [${method}]`) - } -}; - -export default getProjector; diff --git a/federjs_old/FederCore/projector/umap.js b/federjs_old/FederCore/projector/umap.js deleted file mode 100644 index 73da5a0..0000000 --- a/federjs_old/FederCore/projector/umap.js +++ /dev/null @@ -1,32 +0,0 @@ -import { UMAP } from 'umap-js'; - -const fixedParams = { - nComponents: 2, -}; - -export const UMAP_PROJECT_PARAMETERS = { - nComponents: - 'The number of components (dimensions) to project the data to. (default 2)', - nEpochs: - 'The number of epochs to optimize embeddings via SGD. (computed automatically)', - nNeighbors: - 'The number of nearest neighbors to construct the fuzzy manifold. (default 15)', - minDist: - 'The effective minimum distance between embedded points, used with spread to control the clumped/dispersed nature of the embedding. (default 0.1)', - spread: - 'The effective scale of embedded points, used with minDist to control the clumped/dispersed nature of the embedding. (default 1.0)', - random: - 'A pseudo-random-number generator for controlling stochastic processes. (default Math.random())', - distanceFn: 'A custom distance function to use. (default L2)', - url: 'https://github.com/PAIR-code/umap-js', -}; - -export const umapProject = (projectParams = {}) => { - const params = Object.assign({}, projectParams, fixedParams); - return (vectors) => { - const umap = new UMAP(params); - return umap.fit(vectors); - }; -}; - -export default umapProject; diff --git a/federjs_old/FederCore/searchHandler/hnswSearch/index.js b/federjs_old/FederCore/searchHandler/hnswSearch/index.js deleted file mode 100644 index e1b0a91..0000000 --- a/federjs_old/FederCore/searchHandler/hnswSearch/index.js +++ /dev/null @@ -1,78 +0,0 @@ -import { getDisFunc } from 'Utils'; -import { MetricType } from 'Types'; -import searchLevelO from './searchLevel0'; - -const hnswlibHNSWSearch = ({ index, target, params = {} }) => { - const { ef = 10, k = 8, metricType = MetricType.METRIC_L2 } = params; - const disfunc = getDisFunc(metricType); - - let topkResults = []; - const vis_records_all = []; - - const { - enterPoint, - vectors, - maxLevel, - linkLists_levels, - linkLists_level_0, - numDeleted, - labels, - } = index; - - let curNodeId = enterPoint; - let curDist = disfunc(vectors[curNodeId], target); - - for (let level = maxLevel; level > 0; level--) { - const vis_records = []; - vis_records.push([labels[curNodeId], labels[curNodeId], curDist]); - let changed = true; - while (changed) { - changed = false; - - const curlinks = linkLists_levels[curNodeId][level - 1]; - - curlinks.forEach((candidateId) => { - const dist = disfunc(vectors[candidateId], target); - vis_records.push([labels[curNodeId], labels[candidateId], dist]); - if (dist < curDist) { - curDist = dist; - curNodeId = candidateId; - changed = true; - } - }); - } - vis_records_all.push(vis_records); - } - - const hasDeleted = numDeleted > 0; - const { top_candidates, vis_records_level_0 } = searchLevelO({ - ep_id: curNodeId, - target, - vectors, - ef: Math.max(ef, k), - hasDeleted, - linkLists_level_0, - disfunc, - labels, - }); - vis_records_all.push(vis_records_level_0); - - while (top_candidates.size > k) { - top_candidates.pop(); - } - - while (top_candidates.size > 0) { - const res = top_candidates.pop(); - topkResults.push({ - id: labels[res[1]], - internalId: res[1], - dis: -res[0], - }); - } - - topkResults = topkResults.reverse(); - - return { vis_records: vis_records_all, topkResults, searchParams: { k, ef } }; -}; - -export default hnswlibHNSWSearch; diff --git a/federjs_old/FederCore/searchHandler/hnswSearch/searchLevel0.js b/federjs_old/FederCore/searchHandler/hnswSearch/searchLevel0.js deleted file mode 100644 index a835758..0000000 --- a/federjs_old/FederCore/searchHandler/hnswSearch/searchLevel0.js +++ /dev/null @@ -1,82 +0,0 @@ - -import PriorityQueue from 'Utils/PriorityQueue'; - -export const searchLevelO = ({ - ep_id, - target, - vectors, - ef, - isDeleted, - hasDeleted, - linkLists_level_0, - disfunc, - labels, -}) => { - const top_candidates = new PriorityQueue([], (d) => d[0]); - const candidates = new PriorityQueue([], (d) => d[0]); - const vis_records_level_0 = []; - - const visited = new Set(); - - let lowerBound; - if (!hasDeleted || !isDeleted[ep_id]) { - const dist = disfunc(vectors[ep_id], target); - lowerBound = dist; - top_candidates.add([-dist, ep_id]); - candidates.add([dist, ep_id]); - } else { - lowerBound = 9999999; - candidates.add([lowerBound, ep_id]); - } - - visited.add(ep_id); - vis_records_level_0.push([labels[ep_id], labels[ep_id], lowerBound]); - - while (!candidates.isEmpty) { - const curNodePair = candidates.top; - if ( - curNodePair[0] > lowerBound && - (top_candidates.size === ef || !hasDeleted) - ) { - break; - } - candidates.pop(); - - const curNodeId = curNodePair[1]; - const curLinks = linkLists_level_0[curNodeId]; - - curLinks.forEach((candidateId) => { - if (!visited.has(candidateId)) { - visited.add(candidateId); - - const dist = disfunc(vectors[candidateId], target); - vis_records_level_0.push([ - labels[curNodeId], - labels[candidateId], - dist, - ]); - - if (top_candidates.size < ef || lowerBound > dist) { - candidates.add([dist, candidateId]); - - if (!hasDeleted || !isDeleted(candidateId)) { - top_candidates.add([-dist, candidateId]); - } - - if (top_candidates.size > ef) { - top_candidates.pop(); - } - - if (!top_candidates.isEmpty) { - lowerBound = -top_candidates.top[0]; - } - } - } else { - vis_records_level_0.push([labels[curNodeId], labels[candidateId], -1]); - } - }); - } - return { top_candidates, vis_records_level_0 }; -}; - -export default searchLevelO; \ No newline at end of file diff --git a/federjs_old/FederCore/searchHandler/index.js b/federjs_old/FederCore/searchHandler/index.js deleted file mode 100644 index 58cde6c..0000000 --- a/federjs_old/FederCore/searchHandler/index.js +++ /dev/null @@ -1,17 +0,0 @@ -import hnswSearchHandler from './hnswSearch'; -import ivfflatSearchHandler from './ivfflatSearch'; -import { INDEX_TYPE } from 'Types'; - -const indexSearchHandlerMap = { - [INDEX_TYPE.hnsw]: hnswSearchHandler, - [INDEX_TYPE.ivf_flat]: ivfflatSearchHandler, -}; - -export const getIndexSearchHandler = (indexType) => { - if (indexType in indexSearchHandlerMap) { - return indexSearchHandlerMap[indexType]; - } else throw `No search handler for [${indexType}]`; -}; - -export default getIndexSearchHandler; - diff --git a/federjs_old/FederCore/searchHandler/ivfflatSearch/faissFlatSearch.js b/federjs_old/FederCore/searchHandler/ivfflatSearch/faissFlatSearch.js deleted file mode 100644 index d480f77..0000000 --- a/federjs_old/FederCore/searchHandler/ivfflatSearch/faissFlatSearch.js +++ /dev/null @@ -1,13 +0,0 @@ -import { getDisFunc } from 'Utils'; - -export const faissFlatSearch = ({ index, target}) => { - const disFunc = getDisFunc(index.metricType); - const distances = index.vectors.map((vec, id) => ({ - id, - dis: disFunc(vec, target), - })); - distances.sort((a, b) => a.dis - b.dis); - return distances; -}; - -export default faissFlatSearch; diff --git a/federjs_old/FederCore/searchHandler/ivfflatSearch/faissIVFSearch.js b/federjs_old/FederCore/searchHandler/ivfflatSearch/faissIVFSearch.js deleted file mode 100644 index 1904b2c..0000000 --- a/federjs_old/FederCore/searchHandler/ivfflatSearch/faissIVFSearch.js +++ /dev/null @@ -1,23 +0,0 @@ -import { getDisFunc } from 'Utils'; - -export const faissIVFSearch = ({ index, csListIds, target }) => { - const disFunc = getDisFunc(index.metricType); - const distances = index.invlists.data.reduce( - (acc, cur, listId) => - acc.concat( - csListIds.includes(listId) - ? cur.ids.map((id, ofs) => ({ - id, - listId, - dis: disFunc(cur.vectors[ofs], target), - // vec: cur.vectors[ofs] - })) - : [] - ), - [] - ); - distances.sort((a, b) => a.dis - b.dis); - return distances; -}; - -export default faissIVFSearch; diff --git a/federjs_old/FederCore/searchHandler/ivfflatSearch/index.js b/federjs_old/FederCore/searchHandler/ivfflatSearch/index.js deleted file mode 100644 index 48c0b1f..0000000 --- a/federjs_old/FederCore/searchHandler/ivfflatSearch/index.js +++ /dev/null @@ -1,39 +0,0 @@ -import faissFlatSearch from './faissFlatSearch.js'; -import faissIVFSearch from './faissIVFSearch.js'; - -export const faissIVFFlatSearch = ({ index, target, params = {} }) => { - const { nprobe = 8, k = 10 } = params; - - // cs: coarse-search - // fs: fine-search - const csAllListIdsAndDistances = faissFlatSearch({ - index: index.childIndex, - target, - }); - const csRes = csAllListIdsAndDistances.slice( - 0, - Math.min(index.nlist, nprobe) - ); - const csListIds = csRes.map((res) => res.id); - - const fsAllIdsAndDistances = faissIVFSearch({ - index, - csListIds, - target, - }); - // console.log('fsResProjections', fsResProjections); - const fsRes = fsAllIdsAndDistances.slice(0, Math.min(index.ntotal, k)); - - const coarse = csAllListIdsAndDistances; - const fine = fsAllIdsAndDistances; - const res = { - coarse, - fine, - csResIds: csListIds, - fsResIds: fsRes.map((d) => d.id), - }; - - return res; -}; - -export default faissIVFFlatSearch; diff --git a/federjs_old/FederView/BaseView.js b/federjs_old/FederView/BaseView.js deleted file mode 100644 index abfa801..0000000 --- a/federjs_old/FederView/BaseView.js +++ /dev/null @@ -1,109 +0,0 @@ -import * as d3 from 'd3'; -import { renderLoading, finishLoading } from './loading'; -// import { VIEW_TYPE } from 'Types'; - -export default class BaseView { - constructor({ viewParams, getVectorById }) { - this.viewParams = viewParams; - - const { width, height, canvasScale, mediaType, mediaCallback } = viewParams; - this.clientWidth = width; - this.width = width * canvasScale; - this.clientHeight = height; - this.height = height * canvasScale; - this.getVectorById = getVectorById; - this.canvasScale = canvasScale; - this.mediaType = mediaType; - this.mediaCallback = mediaCallback; - } - - // override - initInfoPanel() {} - renderOverview() {} - renderSearchView() {} - searchViewHandler() {} - getOverviewEventHandler() {} - getSearchViewEventHandler() {} - - async overview(dom) { - const canvas = initCanvas( - dom, - this.clientWidth, - this.clientHeight, - this.canvasScale - ); - const ctx = canvas.getContext('2d'); - const infoPanel = this.initInfoPanel(dom); - - this.overviewLayoutPromise && (await this.overviewLayoutPromise); - finishLoading(dom); - this.renderOverview(ctx, infoPanel); - const eventHandlers = this.getOverviewEventHandler(ctx, infoPanel); - addMouseListener(canvas, this.canvasScale, eventHandlers); - } - - async search(dom, { searchRes, targetMediaUrl }) { - const canvas = initCanvas( - dom, - this.clientWidth, - this.clientHeight, - this.canvasScale - ); - const ctx = canvas.getContext('2d'); - const infoPanel = this.initInfoPanel(dom); - - const searchViewLayoutData = await this.searchViewHandler(searchRes); - finishLoading(dom); - this.renderSearchView( - ctx, - infoPanel, - searchViewLayoutData, - targetMediaUrl, - dom - ); - const eventHandlers = this.getSearchViewEventHandler( - ctx, - searchViewLayoutData, - infoPanel - ); - addMouseListener(canvas, this.canvasScale, eventHandlers); - } -} - -const addMouseListener = ( - element, - canvasScale, - { mouseMoveHandler, mouseClickHandler, mouseLeaveHandler } = {} -) => { - element.addEventListener('mousemove', (e) => { - const { offsetX, offsetY } = e; - const x = offsetX * canvasScale; - const y = offsetY * canvasScale; - mouseMoveHandler && mouseMoveHandler({ x, y }); - }); - element.addEventListener('click', (e) => { - const { offsetX, offsetY } = e; - const x = offsetX * canvasScale; - const y = offsetY * canvasScale; - mouseClickHandler && mouseClickHandler({ x, y }); - }); - element.addEventListener('mouseleave', () => { - mouseLeaveHandler && mouseLeaveHandler(); - }); -}; - -const initCanvas = (dom, clientWidth, clientHeight, canvasScale) => { - renderLoading(dom, clientWidth, clientHeight); - - const domD3 = d3.select(dom); - domD3.selectAll('canvas').remove(); - - const canvas = domD3 - .append('canvas') - .attr('width', clientWidth) - .attr('height', clientHeight); - const ctx = canvas.node().getContext('2d'); - ctx.scale(1 / canvasScale, 1 / canvasScale); - - return canvas.node(); -}; diff --git a/federjs_old/FederView/HnswView/InfoPanel/index.js b/federjs_old/FederView/HnswView/InfoPanel/index.js deleted file mode 100644 index 683f55e..0000000 --- a/federjs_old/FederView/HnswView/InfoPanel/index.js +++ /dev/null @@ -1,425 +0,0 @@ -import * as d3 from 'd3'; - -import { - ZYellow, - ZBlue, - whiteColor, - blackColor, - drawPath, - hexWithOpacity, -} from 'Utils/renderUtils'; - -import { showVectors } from 'Utils'; -import { HNSW_NODE_TYPE } from 'Types'; - -export const overviewPanelId = 'feder-info-overview-panel'; -export const selectedPanelId = 'feder-info-selected-panel'; -export const hoveredPanelId = 'feder-info-hovered-panel'; - -const panelBackgroundColor = hexWithOpacity(blackColor, 0.6); - -export default class InfoPanel { - constructor({ dom, width, height }) { - this.dom = dom; - this.width = width; - this.height = height; - - const overviewPanel = document.createElement('div'); - overviewPanel.setAttribute('id', overviewPanelId); - overviewPanel.className = - overviewPanel.className + ' panel-border panel hide'; - const overviewPanelStyle = { - position: 'absolute', - // left: `${canvas.width - 10}px`, - left: '16px', - top: '10px', - width: '280px', - 'max-height': `${height - 20}px`, - overflow: 'auto', - borderColor: whiteColor, - backgroundColor: panelBackgroundColor, - }; - Object.assign(overviewPanel.style, overviewPanelStyle); - dom.appendChild(overviewPanel); - - const selectedPanel = document.createElement('div'); - selectedPanel.setAttribute('id', selectedPanelId); - selectedPanel.className = - selectedPanel.className + ' panel-border panel hide'; - const selectedPanelStyle = { - position: 'absolute', - // left: `${canvas.width - 10}px`, - right: '16px', - top: '10px', - 'max-width': '180px', - 'max-height': `${height - 20}px`, - overflow: 'auto', - borderColor: ZYellow, - backgroundColor: panelBackgroundColor, - }; - Object.assign(selectedPanel.style, selectedPanelStyle); - dom.appendChild(selectedPanel); - - const hoveredPanel = document.createElement('div'); - hoveredPanel.setAttribute('id', hoveredPanelId); - hoveredPanel.className = hoveredPanel.className + ' hide'; - const hoveredPanelStyle = { - position: 'absolute', - left: 0, - // right: '30px', - top: 0, - width: '240px', - display: 'flex', - pointerEvents: 'none', - }; - Object.assign(hoveredPanel.style, hoveredPanelStyle); - dom.appendChild(hoveredPanel); - - this.initStyle(); - } - - initStyle() { - const style = document.createElement('style'); - style.type = 'text/css'; - style.innerHTML = ` - .panel-border { - border-style: dashed; - border-width: 1px; - } - .panel { - padding: 6px 8px; - font-size: 12px; - } - .hide { - opacity: 0; - } - .panel-item { - margin-bottom: 6px; - } - .panel-img { - width: 150px; - height: 100px; - background-size: cover; - margin-bottom: 12px; - border-radius: 4px; - border: 1px solid ${ZYellow}; - } - .panel-item-display-flex { - display: flex; - } - .panel-item-title { - font-weight: 600; - margin-bottom: 3px; - } - .panel-item-text { - font-weight: 400; - font-size: 10px; - word-break: break-all; - } - .panel-item-text-flex { - margin-left: 8px; - } - .panel-item-text-margin { - margin: 0 6px; - } - .text-no-wrap { - white-space: nowrap; - } - `; - document.getElementsByTagName('head').item(0).appendChild(style); - } - - renderSelectedPanel(itemList = [], color = '#000') { - const panel = d3.select(this.dom).select(`#${selectedPanelId}`); - panel.style('color', color); - if (itemList.length === 0) panel.classed('hide', true); - else { - this.renderPanel(panel, itemList); - } - } - renderHoveredPanel({ - itemList = [], - canvasScale = 1, - color = '#000', - x = 0, - y = 0, - isLeft = false, - } = {}) { - const panel = d3.select(this.dom).select(`#${hoveredPanelId}`); - if (itemList.length === 0) panel.classed('hide', true); - else { - panel.style('color', color); - // panel.style.left = x + 'px'; - // panel.style.top = y + 'px'; - if (isLeft) { - panel.style('left', null); - panel.style('right', this.width - x / canvasScale + 'px'); - panel.style('flex-direction', 'row-reverse'); - } else { - panel.style('left', x / canvasScale + 'px'); - panel.style('flex-direction', 'row'); - } - - panel.style('transform', `translateY(-6px)`); - - panel.style('top', y / canvasScale + 'px'); - this.renderPanel(panel, itemList); - } - } - renderOverviewPanel(itemList = [], color) { - const panel = d3.select(this.dom).select(`#${overviewPanelId}`); - panel.style('color', color); - if (itemList.length === 0) panel.classed('hide', true); - else { - this.renderPanel(panel, itemList); - } - } - renderPanel(panel, itemList) { - panel.classed('hide', false); - panel.selectAll('*').remove(); - - itemList.forEach((item) => { - const div = panel.append('div'); - div.classed('panel-item', true); - item.isFlex && div.classed('panel-item-display-flex', true); - if (item.isImg && item.imgUrl) { - div.classed('panel-img', true); - div.style('background-image', `url(${item.imgUrl})`); - } - if (item.title) { - const title = div.append('div'); - title.classed('panel-item-title', true); - title.text(item.title); - } - if (item.text) { - const title = div.append('div'); - title.classed('panel-item-text', true); - item.isFlex && title.classed('panel-item-text-flex', true); - item.textWithMargin && title.classed('panel-item-text-margin', true); - item.noWrap && title.classed('text-no-wrap', true); - title.text(item.text); - } - }); - } - - updateOverviewOverviewInfo({ indexMeta, nodesCount, linksCount }) { - const overviewInfo = [ - { - title: 'HNSW', - }, - { - title: `M = ${indexMeta.M}, ef_construction = ${indexMeta.ef_construction}`, - }, - { - title: `${indexMeta.ntotal} vectors, including ${indexMeta.levelCount} levels (only show the top 3 levels).`, - }, - ]; - for (let level = indexMeta.overviewLevelCount - 1; level >= 0; level--) { - overviewInfo.push({ - isFlex: true, - title: `Level ${ - level + indexMeta.levelCount - indexMeta.overviewLevelCount - }`, - text: `${nodesCount[level]} vectors, ${linksCount[level]} links`, - }); - } - this.renderOverviewPanel(overviewInfo, whiteColor); - } - async updateOverviewClickedInfo( - node, - level, - { indexMeta, mediaType, mediaCallback, getVectorById } - ) { - const itemList = []; - if (node) { - itemList.push({ - title: `Level ${ - level + indexMeta.levelCount - indexMeta.overviewLevelCount - }`, - }); - itemList.push({ - title: `Row No. ${node.id}`, - }); - mediaType === 'img' && - itemList.push({ - isImg: true, - imgUrl: mediaCallback(node.id), - }); - itemList.push({ - title: `Shortest path from the entry:`, - text: `${[...node.path, node.id].join(' => ')}`, - }); - itemList.push({ - title: `Linked vectors:`, - text: `${node.linksLevels[level].join(', ')}`, - }); - itemList.push({ - title: `Vectors:`, - text: `${showVectors(await getVectorById(node.id))}`, - }); - } - this.renderSelectedPanel(itemList, ZYellow); - } - updateOverviewHoveredInfo( - hoveredNode, - { isLeft, endX, endY }, - { mediaType, mediaCallback, canvasScale } - ) { - if (!!hoveredNode) { - const itemList = []; - itemList.push({ - text: `No. ${hoveredNode.id}`, - textWithMargin: true, - noWrap: true, - }); - mediaType === 'img' && - itemList.push({ - isImg: true, - imgUrl: mediaCallback(hoveredNode.id), - }); - - this.renderHoveredPanel({ - itemList, - color: ZYellow, - x: endX, - y: endY, - isLeft, - canvasScale, - }); - } else { - this.renderHoveredPanel(); - } - } - - updateSearchViewOverviewInfo( - { - targetMediaUrl, - id2forcePos, - searchNodesLevels, - searchLinksLevels, - searchParams, - }, - { indexMeta } - ) { - const overviewInfo = [ - { - title: 'HNSW - Search', - }, - { - isImg: true, - imgUrl: targetMediaUrl, - }, - { - title: `M = ${indexMeta.M}, ef_construction = ${indexMeta.ef_construction}.`, - }, - { - title: `k = ${searchParams.k}, ef_search = ${searchParams.ef}.`, - }, - { - title: `${indexMeta.ntotal} vectors, including ${indexMeta.levelCount} levels.`, - }, - { - title: `During the search, a total of ${ - Object.keys(id2forcePos).length - 1 - } of these vectors were visited.`, - }, - ]; - for (let level = indexMeta.levelCount - 1; level >= 0; level--) { - const nodes = searchNodesLevels[level]; - const links = searchLinksLevels[level]; - const minDist = - level > 0 - ? nodes - .find((node) => node.type === HNSW_NODE_TYPE.Fine) - .dist.toFixed(3) - : d3.min( - nodes.filter((node) => node.type === HNSW_NODE_TYPE.Fine), - (node) => node.dist.toFixed(3) - ); - overviewInfo.push({ - isFlex: true, - title: `Level ${level}`, - text: `${nodes.length} vectors, ${links.length} links, min-dist: ${minDist}`, - }); - } - this.renderOverviewPanel(overviewInfo, whiteColor); - } - updateSearchViewHoveredInfo( - { hoveredNode, hoveredLevel }, - { - mediaType, - mediaCallback, - width, - padding, - - HoveredPanelLine_1_x, - HoveredPanelLine_1_y, - HoveredPanelLine_2_x, - canvasScale, - } - ) { - if (!hoveredNode) { - this.renderHoveredPanel(); - } else { - const [x, y] = hoveredNode.searchViewPosLevels[hoveredLevel]; - const originX = (width - padding[1] - padding[3]) / 2 + padding[3]; - const isLeft = originX > x; - const k = isLeft ? -1 : 1; - const endX = - x + - HoveredPanelLine_1_x * canvasScale * k + - HoveredPanelLine_2_x * canvasScale * k; - const endY = y + HoveredPanelLine_1_y * canvasScale * k; - - const itemList = []; - itemList.push({ - text: `No. ${hoveredNode.id}`, - textWithMargin: true, - noWrap: true, - }); - mediaType === 'img' && - itemList.push({ - isImg: true, - imgUrl: mediaCallback(hoveredNode.id), - }); - - this.renderHoveredPanel({ - itemList, - color: ZYellow, - x: endX, - y: endY, - isLeft, - canvasScale, - }); - } - } - async updateSearchViewClickedInfo( - { clickedNode, clickedLevel }, - { mediaType, mediaCallback, getVectorById } - ) { - const itemList = []; - if (!clickedNode) { - this.renderSelectedPanel([], ZYellow); - } else { - itemList.push({ - title: `Level ${clickedLevel}`, - }); - itemList.push({ - title: `Row No. ${clickedNode.id}`, - }); - itemList.push({ - title: `Distance to the target: ${clickedNode.dist.toFixed(3)}`, - }); - mediaType === 'img' && - itemList.push({ - isImg: true, - imgUrl: mediaCallback(clickedNode.id), - }); - itemList.push({ - title: `Vector:`, - text: `${showVectors(await getVectorById(clickedNode.id))}`, - }); - } - this.renderSelectedPanel(itemList, ZYellow); - } -} diff --git a/federjs_old/FederView/HnswView/index.js b/federjs_old/FederView/HnswView/index.js deleted file mode 100644 index 5d6e740..0000000 --- a/federjs_old/FederView/HnswView/index.js +++ /dev/null @@ -1,257 +0,0 @@ -import * as d3 from 'd3'; -import BaseView from '../BaseView.js'; -import overviewLayoutHandler from './layout/overviewLayout'; -import mouse2node from './layout/mouse2node'; -import renderOverview from './render/renderOverview'; -import getOverviewShortestPathData from './layout/overviewShortestPath'; -import searchViewLayoutHandler from './layout/searchViewLayout'; -import TimeControllerView from './render/TimeControllerView'; -import TimerController from './render/TimerController'; -import renderHoverLine from './render/renderHoverLine'; -import renderSearchViewTransition from './render/renderSearchViewTransition'; -import InfoPanel from './InfoPanel'; - -const defaultHnswViewParams = { - padding: [80, 200, 60, 220], - forceTime: 3000, - layerDotNum: 20, - shortenLineD: 8, - overviewLinkLineWidth: 2, - reachableLineWidth: 3, - shortestPathLineWidth: 4, - ellipseRation: 1.4, - shadowBlur: 4, - mouse2nodeBias: 3, - highlightRadiusExt: 0.5, - targetR: 3, - searchViewNodeBasicR: 1.5, - searchInterLevelTime: 300, - searchIntraLevelTime: 100, - HoveredPanelLine_1_x: 15, - HoveredPanelLine_1_y: -25, - HoveredPanelLine_2_x: 30, - hoveredPanelLineWidth: 2, - forceIterations: 100, - targetOrigin: [0, 0], -}; -export default class HnswView extends BaseView { - constructor({ indexMeta, viewParams, getVectorById }) { - super({ - indexMeta, - viewParams, - getVectorById, - }); - for (let key in defaultHnswViewParams) { - this[key] = - key in viewParams ? viewParams[key] : defaultHnswViewParams[key]; - } - this.padding = this.padding.map((num) => num * this.canvasScale); - - this.overviewHandler(indexMeta); - } - initInfoPanel(dom) { - const infoPanel = new InfoPanel({ - dom, - width: this.viewParams.width, - height: this.viewParams.height, - }); - return infoPanel; - } - overviewHandler(indexMeta) { - console.log(indexMeta); - this.indexMeta = indexMeta; - Object.assign(this, indexMeta); - - const internalId2overviewNode = {}; - this.overviewNodes.forEach( - (node) => (internalId2overviewNode[node.internalId] = node) - ); - this.internalId2overviewNode = internalId2overviewNode; - - this.overviewLayoutPromise = overviewLayoutHandler(this).then( - ({ - overviewLayerPosLevels, - overviewNodesLevels, - overviewLinksLevels, - }) => { - this.overviewLayerPosLevels = overviewLayerPosLevels; - this.overviewNodesLevels = overviewNodesLevels; - this.overviewLinksLevels = overviewLinksLevels; - } - ); - } - renderOverview(ctx, infoPanel) { - const indexMeta = this.indexMeta; - const nodesCount = this.overviewNodesLevels.map( - (nodesLevel) => nodesLevel.length - ); - const linksCount = this.overviewLinksLevels.map( - (linksLevel) => linksLevel.length - ); - const overviewInfo = { indexMeta, nodesCount, linksCount }; - infoPanel.updateOverviewOverviewInfo(overviewInfo); - - // this.searchTransitionTimer && this.searchTransitionTimer.stop(); - renderOverview(ctx, this); - } - getOverviewEventHandler(ctx, infoPanel) { - let clickedNode = null; - let clickedLevel = null; - let hoveredNode = null; - let hoveredLevel = null; - let overviewHighlightData = null; - const mouseMoveHandler = ({ x, y }) => { - const mouse = [x, y]; - const { mouseLevel, mouseNode } = mouse2node( - { - mouse, - layerPosLevels: this.overviewLayerPosLevels, - nodesLevels: this.overviewNodesLevels, - posAttr: 'overviewPosLevels', - }, - this - ); - const hoveredNodeChanged = - hoveredLevel !== mouseLevel || hoveredNode !== mouseNode; - hoveredNode = mouseNode; - hoveredLevel = mouseLevel; - - if (hoveredNodeChanged) { - if (!clickedNode) { - overviewHighlightData = getOverviewShortestPathData( - mouseNode, - mouseLevel, - this - ); - } - renderOverview(ctx, this, overviewHighlightData); - const hoveredPanelPos = renderHoverLine( - ctx, - { - hoveredNode, - hoveredLevel, - clickedNode, - clickedLevel, - }, - this - ); - infoPanel.updateOverviewHoveredInfo(mouseNode, hoveredPanelPos, this); - } - }; - const mouseClickHandler = ({ x, y }) => { - const mouse = [x, y]; - const { mouseLevel, mouseNode } = mouse2node( - { - mouse, - layerPosLevels: this.overviewLayerPosLevels, - nodesLevels: this.overviewNodesLevels, - posAttr: 'overviewPosLevels', - }, - this - ); - const clickedNodeChanged = - clickedLevel !== mouseLevel || clickedNode !== mouseNode; - clickedNode = mouseNode; - clickedLevel = mouseLevel; - - if (clickedNodeChanged) { - overviewHighlightData = getOverviewShortestPathData( - mouseNode, - mouseLevel, - this - ); - renderOverview(ctx, this, overviewHighlightData); - infoPanel.updateOverviewClickedInfo(mouseNode, mouseLevel, this); - } - }; - return { mouseMoveHandler, mouseClickHandler }; - } - - searchViewHandler(searchRes) { - return searchViewLayoutHandler(searchRes, this); - } - renderSearchView(ctx, infoPanel, searchViewLayoutData, targetMediaUrl, dom) { - searchViewLayoutData.targetMediaUrl = targetMediaUrl; - infoPanel.updateSearchViewOverviewInfo(searchViewLayoutData, this); - const timeControllerView = new TimeControllerView(dom); - - const callback = ({ t, p }) => { - renderSearchViewTransition(ctx, searchViewLayoutData, this, { - t, - p, - }); - timeControllerView.moveSilderBar(p); - }; - const timer = new TimerController({ - duration: searchViewLayoutData.searchTransitionDuration, - callback, - playCallback: () => timeControllerView.play(), - pauseCallback: () => timeControllerView.pause(), - }); - timeControllerView.setTimer(timer); - timer.start(); - searchViewLayoutData.searchTransitionTimer = timer; - } - getSearchViewEventHandler(ctx, searchViewLayoutData, infoPanel) { - searchViewLayoutData.clickedLevel = null; - searchViewLayoutData.clickedNode = null; - searchViewLayoutData.hoveredLevel = null; - searchViewLayoutData.hoveredNode = null; - - const mouseMoveHandler = ({ x, y }) => { - const mouse = [x, y]; - const { mouseLevel, mouseNode } = mouse2node( - { - mouse, - layerPosLevels: searchViewLayoutData.searchLayerPosLevels, - nodesLevels: searchViewLayoutData.searchNodesLevels, - posAttr: 'searchViewPosLevels', - }, - this - ); - const hoveredNodeChanged = - searchViewLayoutData.hoveredLevel !== mouseLevel || - searchViewLayoutData.hoveredNode !== mouseNode; - searchViewLayoutData.hoveredNode = mouseNode; - searchViewLayoutData.hoveredLevel = mouseLevel; - - if (hoveredNodeChanged) { - infoPanel.updateSearchViewHoveredInfo(searchViewLayoutData, this); - if (!searchViewLayoutData.searchTransitionTimer.isPlaying) { - renderSearchViewTransition(ctx, searchViewLayoutData, this, { - t: searchViewLayoutData.searchTransitionTimer.tAlready, - }); - } - } - }; - - const mouseClickHandler = ({ x, y }) => { - const mouse = [x, y]; - const { mouseLevel, mouseNode } = mouse2node( - { - mouse, - layerPosLevels: searchViewLayoutData.searchLayerPosLevels, - nodesLevels: searchViewLayoutData.searchNodesLevels, - posAttr: 'searchViewPosLevels', - }, - this - ); - const clickedNodeChanged = - searchViewLayoutData.clickedLevel !== mouseLevel || - searchViewLayoutData.clickedNode !== mouseNode; - searchViewLayoutData.clickedNode = mouseNode; - searchViewLayoutData.clickedLevel = mouseLevel; - - if (clickedNodeChanged) { - infoPanel.updateSearchViewClickedInfo(searchViewLayoutData, this); - if (!searchViewLayoutData.searchTransitionTimer.isPlaying) { - renderSearchViewTransition(ctx, searchViewLayoutData, this, { - t: searchViewLayoutData.searchTransitionTimer.tAlready, - }); - } - } - }; - - return { mouseMoveHandler, mouseClickHandler }; - } -} diff --git a/federjs_old/FederView/HnswView/layout/computeSearchViewTransition.js b/federjs_old/FederView/HnswView/layout/computeSearchViewTransition.js deleted file mode 100644 index 9d914c9..0000000 --- a/federjs_old/FederView/HnswView/layout/computeSearchViewTransition.js +++ /dev/null @@ -1,86 +0,0 @@ -import { - getNodeIdWithLevel, - getLinkIdWithLevel, - getEntryLinkIdWithLevel, -} from 'Utils'; - -import { HNSW_LINK_TYPE } from 'Types'; - -export const computeSearchViewTransition = ({ - linksLevels, - entryNodesLevels, - interLevelGap = 1000, - intraLevelGap = 300, -}) => { - let currentTime = 0; - const targetShowTime = []; - const nodeShowTime = {}; - const linkShowTime = {}; - - let isPreLinkImportant = true; - let isSourceChanged = true; - let preSourceIdWithLevel = ''; - for (let level = linksLevels.length - 1; level >= 0; level--) { - const links = linksLevels[level]; - if (links.length === 0) { - const sourceId = entryNodesLevels[level].id; - const sourceIdWithLevel = getNodeIdWithLevel(sourceId, level); - nodeShowTime[sourceIdWithLevel] = currentTime; - } else { - links.forEach((link) => { - const sourceId = link.source.id; - const targetId = link.target.id; - const sourceIdWithLevel = getNodeIdWithLevel(sourceId, level); - const targetIdWithLevel = getNodeIdWithLevel(targetId, level); - const linkIdWithLevel = getLinkIdWithLevel(sourceId, targetId, level); - const isCurrentLinkImportant = - link.type === HNSW_LINK_TYPE.Searched || - link.type === HNSW_LINK_TYPE.Fine; - isSourceChanged = preSourceIdWithLevel !== sourceIdWithLevel; - - const isSourceEntry = !(sourceIdWithLevel in nodeShowTime); - if (isSourceEntry) { - if (level < linksLevels.length - 1) { - const entryLinkIdWithLevel = getEntryLinkIdWithLevel( - sourceId, - level - ); - linkShowTime[entryLinkIdWithLevel] = currentTime; - const targetLinkIdWithLevel = getEntryLinkIdWithLevel( - 'target', - level - ); - linkShowTime[targetLinkIdWithLevel] = currentTime; - currentTime += interLevelGap; - isPreLinkImportant = true; - } - targetShowTime[level] = currentTime; - nodeShowTime[sourceIdWithLevel] = currentTime; - } - - // something wrong - if (isPreLinkImportant || isCurrentLinkImportant || isSourceChanged) { - currentTime += intraLevelGap; - } else { - currentTime += intraLevelGap * 0.5; - } - - linkShowTime[linkIdWithLevel] = currentTime; - - if (!(targetIdWithLevel in nodeShowTime)) { - nodeShowTime[targetIdWithLevel] = currentTime += intraLevelGap; - } - - isPreLinkImportant = isCurrentLinkImportant; - preSourceIdWithLevel = sourceIdWithLevel; - }); - } - - currentTime += intraLevelGap; - isPreLinkImportant = true; - isSourceChanged = true; - } - return { targetShowTime, nodeShowTime, linkShowTime, duration: currentTime }; -}; - -export default computeSearchViewTransition; diff --git a/federjs_old/FederView/HnswView/layout/forceSearchView.js b/federjs_old/FederView/HnswView/layout/forceSearchView.js deleted file mode 100644 index 6863b79..0000000 --- a/federjs_old/FederView/HnswView/layout/forceSearchView.js +++ /dev/null @@ -1,81 +0,0 @@ -import * as d3 from 'd3'; -import { HNSW_LINK_TYPE } from 'Types'; -import { deDupLink } from 'Utils'; - -const forceSearchView = ( - visData, - targetOrigin = [0, 0], - forceIterations = 100 -) => { - return new Promise((resolve) => { - const nodeId2dist = {}; - visData.forEach((levelData) => - levelData.nodes.forEach((node) => (nodeId2dist[node.id] = node.dist || 0)) - ); - const nodeIds = Object.keys(nodeId2dist); - const nodes = nodeIds.map((nodeId) => ({ - nodeId: nodeId, - dist: nodeId2dist[nodeId], - })); - - const linksAll = visData.reduce((acc, cur) => acc.concat(cur.links), []); - const links = deDupLink(linksAll); - // console.log(nodes, links); - // console.log(links.length, linksAll.length); - - const targetNode = { - nodeId: 'target', - dist: 0, - fx: targetOrigin[0], - fy: targetOrigin[1], - }; - nodes.push(targetNode); - - const targetLinks = visData[0].fineIds.map((fineId) => ({ - source: `${fineId}`, - target: 'target', - type: HNSW_LINK_TYPE.None, - })); - links.push(...targetLinks); - // console.log(nodes, links); - - const rScale = d3 - .scaleLinear() - .domain( - d3.extent( - nodes.filter((node) => node.dist > 0), - (node) => node.dist - ) - ) - .range([10, 1000]) - .clamp(true); - const simulation = d3 - .forceSimulation(nodes) - .alphaDecay(1 - Math.pow(0.001, 1 / forceIterations)) - .force( - 'link', - d3 - .forceLink(links) - .id((d) => `${d.nodeId}`) - .strength((d) => (d.type === HNSW_LINK_TYPE.None ? 2 : 0.4)) - ) - .force( - 'r', - d3 - .forceRadial( - (node) => rScale(node.dist), - targetOrigin[0], - targetOrigin[1] - ) - .strength(1) - ) - .force('charge', d3.forceManyBody().strength(-10000)) - .on('end', () => { - const id2forcePos = {}; - nodes.forEach((node) => (id2forcePos[node.nodeId] = [node.x, node.y])); - resolve(id2forcePos); - }); - }); -}; - -export default forceSearchView; diff --git a/federjs_old/FederView/HnswView/layout/mouse2node.js b/federjs_old/FederView/HnswView/layout/mouse2node.js deleted file mode 100644 index e340615..0000000 --- a/federjs_old/FederView/HnswView/layout/mouse2node.js +++ /dev/null @@ -1,27 +0,0 @@ -import * as d3 from 'd3'; -import { dist2 } from 'Utils'; - -export default function mouse2node( - { mouse, layerPosLevels, nodesLevels, posAttr }, - { mouse2nodeBias, canvasScale } -) { - const mouseLevel = layerPosLevels.findIndex((points) => - d3.polygonContains(points, mouse) - ); - let mouseNode; - if (mouseLevel >= 0) { - const allDis = nodesLevels[mouseLevel].map((node) => - dist2(node[posAttr][mouseLevel], mouse) - ); - const minDistIndex = d3.minIndex(allDis); - const minDist = allDis[minDistIndex]; - const clearestNode = nodesLevels[mouseLevel][minDistIndex]; - mouseNode = - minDist < Math.pow((clearestNode.r + mouse2nodeBias) * canvasScale, 2) - ? clearestNode - : null; - } else { - mouseNode = null; - } - return { mouseLevel, mouseNode }; -} diff --git a/federjs_old/FederView/HnswView/layout/overviewLayout.js b/federjs_old/FederView/HnswView/layout/overviewLayout.js deleted file mode 100644 index d08620c..0000000 --- a/federjs_old/FederView/HnswView/layout/overviewLayout.js +++ /dev/null @@ -1,103 +0,0 @@ -import * as d3 from 'd3'; -import transformHandler from './transformHandler'; - -export const overviewLayoutHandler = ({ - overviewNodes, - overviewLevelCount, - width, - height, - padding, - forceIterations, - M, -}) => { - return new Promise(async (resolve) => { - const overviewNodesLevels = []; - const overviewLinksLevels = []; - for (let level = overviewLevelCount - 1; level >= 0; level--) { - const nodes = overviewNodes.filter( - (node) => node.linksLevels.length > level - ); - const links = nodes.reduce( - (acc, curNode) => - acc.concat( - curNode.linksLevels[level].map((targetNodeInternalId) => ({ - source: curNode.internalId, - target: targetNodeInternalId, - })) - ), - [] - ); - await forceLevel({ nodes, links, forceIterations }); - level > 0 && scaleNodes({ nodes, M }); - level > 0 && fixedCurLevel({ nodes }); - overviewNodesLevels[level] = nodes; - overviewLinksLevels[level] = links; - } - - const { layerPosLevels: overviewLayerPosLevels, transformFunc } = - transformHandler(overviewNodes, { - levelCount: overviewLevelCount, - width, - height, - padding, - }); - - overviewNodes.forEach((node) => { - node.overviewPosLevels = node.linksLevels.map((_, level) => - transformFunc(node.x, node.y, level) - ); - node.r = node.linksLevels.length * 0.8 + 1; - }); - - resolve({ - overviewLayerPosLevels, - overviewNodesLevels, - overviewLinksLevels, - }); - }); -}; - -export default overviewLayoutHandler; - -export const forceLevel = ({ nodes, links, forceIterations }) => { - return new Promise((resolve) => { - const simulation = d3 - .forceSimulation(nodes) - .alphaDecay(1 - Math.pow(0.001, (1 / forceIterations) * 2)) - .force( - 'link', - d3 - .forceLink(links) - .id((d) => d.internalId) - .strength(1) - ) - .force('center', d3.forceCenter(0, 0)) - .force('charge', d3.forceManyBody().strength(-500)) - .on('end', () => { - resolve(); - }); - }); -}; - -export const scaleNodes = ({ nodes, M }) => { - const xRange = d3.extent(nodes, (node) => node.x); - const yRange = d3.extent(nodes, (node) => node.y); - - const isXLonger = xRange[1] - xRange[0] > yRange[1] - yRange[0]; - if (!isXLonger) { - nodes.forEach((node) => ([node.x, node.y] = [node.y, node.x])); - } - - const t = Math.sqrt(M) * 0.85; - nodes.forEach((node) => { - node.x = node.x * t; - node.y = node.y * t; - }); -}; - -export const fixedCurLevel = ({ nodes }) => { - nodes.forEach((node) => { - node.fx = node.x; - node.fy = node.y; - }); -}; diff --git a/federjs_old/FederView/HnswView/layout/overviewShortestPath.js b/federjs_old/FederView/HnswView/layout/overviewShortestPath.js deleted file mode 100644 index 3ab2f53..0000000 --- a/federjs_old/FederView/HnswView/layout/overviewShortestPath.js +++ /dev/null @@ -1,65 +0,0 @@ -export default function getOverviewShortestPathData( - keyNode, - keyLevel, - { overviewNodesLevels, internalId2overviewNode, overviewLevelCount } -) { - let SPLinksLevels = overviewNodesLevels.map((_) => []); - let SPNodesLevels = overviewNodesLevels.map((_) => []); - let reachableNodes = []; - let reachableLinks = []; - let reachableLevel = null; - if (keyNode) { - const path = [...keyNode.path, keyNode.internalId]; - if (path.length === 0) { - SPNodesLevels = [keyNode.overviewPosLevels[keyLevel]]; - } else { - let preNodeId = path[0]; - let preNode = internalId2overviewNode[preNodeId]; - let preLevel = overviewLevelCount - 1; - SPNodesLevels[preLevel].push(preNode); - for (let i = 1; i < path.length; i++) { - let curNodeId = path[i]; - let curNode = internalId2overviewNode[curNodeId]; - while (curNode.overviewPosLevels.length <= preLevel) { - preLevel -= 1; - SPLinksLevels[preLevel].push({ - source: preNode, - target: preNode, - }); - SPNodesLevels[preLevel].push(preNode); - } - SPNodesLevels[preLevel].push(curNode); - SPLinksLevels[preLevel].push({ - source: preNode, - target: curNode, - }); - preNode = curNode; - } - while (preLevel > keyLevel) { - preLevel -= 1; - SPLinksLevels[preLevel].push({ - source: preNode, - target: preNode, - }); - SPNodesLevels[preLevel].push(preNode); - } - } - const preNodeInternalId = - keyNode.path.length > 0 ? keyNode.path[keyNode.path.length - 1] : null; - reachableLevel = keyLevel; - reachableNodes = keyNode.linksLevels[keyLevel] - .filter((internalId) => internalId != preNodeInternalId) - .map((internalId) => internalId2overviewNode[internalId]); - reachableLinks = reachableNodes.map((target) => ({ - source: keyNode, - target, - })); - } - return { - SPLinksLevels, - SPNodesLevels, - reachableLevel, - reachableNodes, - reachableLinks, - }; -} diff --git a/federjs_old/FederView/HnswView/layout/parseVisRecords.js b/federjs_old/FederView/HnswView/layout/parseVisRecords.js deleted file mode 100644 index 1630d21..0000000 --- a/federjs_old/FederView/HnswView/layout/parseVisRecords.js +++ /dev/null @@ -1,105 +0,0 @@ -import { HNSW_NODE_TYPE, HNSW_LINK_TYPE } from 'Types'; -import { getLinkId, parseLinkId } from 'Utils'; - -export const parseVisRecords = ({ topkResults, vis_records }) => { - const visData = []; - const numLevels = vis_records.length; - let fineIds = topkResults.map((d) => d.id); - let entryId = -1; - for (let i = numLevels - 1; i >= 0; i--) { - const level = numLevels - 1 - i; - if (level > 0) { - fineIds = [entryId]; - } - - const visRecordsLevel = vis_records[i]; - - const id2nodeType = {}; - const linkId2linkType = {}; - const updateNodeType = (nodeId, type) => { - if (id2nodeType[nodeId]) { - id2nodeType[nodeId] = Math.max(id2nodeType[nodeId], type); - } else { - id2nodeType[nodeId] = type; - } - }; - const updateLinkType = (sourceId, targetId, type) => { - const linkId = getLinkId(sourceId, targetId, type); - if (linkId2linkType[linkId]) { - linkId2linkType[linkId] = Math.max(linkId2linkType[linkId], type); - } else { - linkId2linkType[linkId] = type; - } - }; - const id2dist = {}; - const sourceMap = {}; - - visRecordsLevel.forEach((record) => { - const [sourceId, targetId, dist] = record; - - if (sourceId === targetId) { - // entry - entryId = targetId; - id2dist[targetId] = dist; - } else { - updateNodeType(sourceId, HNSW_NODE_TYPE.Candidate); - updateNodeType(targetId, HNSW_NODE_TYPE.Coarse); - - if (id2dist[targetId] >= 0) { - // visited - updateLinkType(sourceId, targetId, HNSW_LINK_TYPE.Visited); - } else { - // not visited - id2dist[targetId] = dist; - updateLinkType(sourceId, targetId, HNSW_LINK_TYPE.Extended); - - sourceMap[targetId] = sourceId; - - // only level-0 have "link_type - search" - if (level === 0) { - const preSourceId = sourceMap[sourceId]; - if (preSourceId >= 0) { - updateLinkType(preSourceId, sourceId, HNSW_LINK_TYPE.Searched); - } - } - } - } - }); - - fineIds.forEach((fineId) => { - updateNodeType(fineId, HNSW_NODE_TYPE.Fine); - - let t = fineId; - while (t in sourceMap) { - let s = sourceMap[t]; - updateLinkType(s, t, HNSW_LINK_TYPE.Fine); - t = s; - } - }); - - const nodes = Object.keys(id2nodeType).map((id) => ({ - id: `${id}`, - type: id2nodeType[id], - dist: id2dist[id], - })); - const links = Object.keys(linkId2linkType).map((linkId) => { - const [source, target] = parseLinkId(linkId); - return { - source: `${source}`, - target: `${target}`, - type: linkId2linkType[linkId], - }; - }); - const visDataLevel = { - entryIds: [`${entryId}`], - fineIds: fineIds.map((id) => `${id}`), - links, - nodes, - }; - visData.push(visDataLevel); - } - - return visData; -}; - -export default parseVisRecords; diff --git a/federjs_old/FederView/HnswView/layout/searchViewLayout.js b/federjs_old/FederView/HnswView/layout/searchViewLayout.js deleted file mode 100644 index f00ca6b..0000000 --- a/federjs_old/FederView/HnswView/layout/searchViewLayout.js +++ /dev/null @@ -1,102 +0,0 @@ -import * as d3 from 'd3'; -import parseVisRecords from './parseVisRecords'; -import forceSearchView from './forceSearchView'; -import transformHandler from './transformHandler'; -import computeSearchViewTransition from './computeSearchViewTransition'; -import { HNSW_LINK_TYPE, HNSW_NODE_TYPE } from 'Types'; - -export default async function searchViewLayoutHandler(searchRes, federView) { - const { - targetR, - canvasScale, - targetOrigin, - searchViewNodeBasicR, - searchInterLevelTime, - searchIntraLevelTime, - forceIterations, - } = federView; - - const visData = parseVisRecords(searchRes); - - const id2forcePos = await forceSearchView( - visData, - targetOrigin, - forceIterations - ); - - const searchNodesLevels = visData.map((levelData) => levelData.nodes); - searchNodesLevels.forEach((levelData) => - levelData.forEach((node) => { - node.forcePos = id2forcePos[node.id]; - node.x = node.forcePos[0]; - node.y = node.forcePos[1]; - }) - ); - const { layerPosLevels, transformFunc } = transformHandler( - searchNodesLevels.reduce((acc, node) => acc.concat(node), []), - federView - ); - - const searchTarget = { - id: 'target', - r: targetR * canvasScale, - searchViewPosLevels: d3 - .range(visData.length) - .map((i) => transformFunc(...targetOrigin, i)), - }; - - searchNodesLevels.forEach((nodes, level) => { - nodes.forEach((node) => { - node.searchViewPosLevels = d3 - .range(level + 1) - .map((i) => transformFunc(...node.forcePos, i)); - node.r = (searchViewNodeBasicR + node.type * 0.5) * canvasScale; - }); - }); - - const id2searchNode = {}; - searchNodesLevels.forEach((levelData) => - levelData.forEach((node) => (id2searchNode[node.id] = node)) - ); - - const searchLinksLevels = parseVisRecords(searchRes).map((levelData) => - levelData.links.filter((link) => link.type !== HNSW_LINK_TYPE.None) - ); - searchLinksLevels.forEach((levelData) => - levelData.forEach((link) => { - const sourceId = link.source; - const targetId = link.target; - const sourceNode = id2searchNode[sourceId]; - const targetNode = id2searchNode[targetId]; - link.source = sourceNode; - link.target = targetNode; - }) - ); - - const entryNodesLevels = visData.map((levelData) => - levelData.entryIds.map((id) => id2searchNode[id]) - ); - - const { targetShowTime, nodeShowTime, linkShowTime, duration } = - computeSearchViewTransition({ - linksLevels: searchLinksLevels, - entryNodesLevels, - interLevelGap: searchInterLevelTime, - intraLevelGap: searchIntraLevelTime, - }); - - return { - visData, - id2forcePos, - searchTarget, - entryNodesLevels, - searchNodesLevels, - searchLinksLevels, - searchLayerPosLevels: layerPosLevels, - searchTargetShowTime: targetShowTime, - searchNodeShowTime: nodeShowTime, - searchLinkShowTime: linkShowTime, - searchTransitionDuration: duration, - searchParams: searchRes.searchParams, - }; -} diff --git a/federjs_old/FederView/HnswView/layout/transformHandler.js b/federjs_old/FederView/HnswView/layout/transformHandler.js deleted file mode 100644 index dd10153..0000000 --- a/federjs_old/FederView/HnswView/layout/transformHandler.js +++ /dev/null @@ -1,48 +0,0 @@ -import * as d3 from 'd3'; -import { generateArray } from 'Utils'; - -export const transformHandler = ( - nodes, - { levelCount, width, height, padding, xBias = 0.65, yBias = 0.4, yOver = 0.1 } -) => { - const layerWidth = width - padding[1] - padding[3]; - const layerHeight = - (height - padding[0] - padding[2]) / - (levelCount - (levelCount - 1) * yOver); - const xRange = d3.extent(nodes, (node) => node.x); - const yRange = d3.extent(nodes, (node) => node.y); - - const xOffset = padding[3] + layerWidth * xBias; - const transformFunc = (x, y, level) => { - const _x = (x - xRange[0]) / (xRange[1] - xRange[0]); - const _y = (y - yRange[0]) / (yRange[1] - yRange[0]); - - const newX = - xOffset + _x * layerWidth * (1 - xBias) - _y * layerWidth * xBias; - - const newY = - padding[0] + - layerHeight * (1 - yOver) * (levelCount - 1 - level) + - _x * layerHeight * (1 - yBias) + - _y * layerHeight * yBias; - - return [newX, newY]; - }; - const layerPos = [ - [layerWidth * xBias, 0], - [layerWidth, layerHeight * (1 - yBias)], - [layerWidth * (1 - xBias), layerHeight], - [0, layerHeight * yBias], - ]; - const layerPosLevels = generateArray(levelCount).map((_, level) => - layerPos.map((coord) => [ - coord[0] + padding[3], - coord[1] + - padding[0] + - layerHeight * (1 - yOver) * (levelCount - 1 - level), - ]) - ); - return { layerPosLevels, transformFunc }; -}; - -export default transformHandler; diff --git a/federjs_old/FederView/HnswView/render/TimeControllerView.js b/federjs_old/FederView/HnswView/render/TimeControllerView.js deleted file mode 100644 index ececbfb..0000000 --- a/federjs_old/FederView/HnswView/render/TimeControllerView.js +++ /dev/null @@ -1,166 +0,0 @@ -import * as d3 from 'd3'; - -const iconGap = 10; -const rectW = 36; -const sliderBackGroundWidth = 200; -const sliderWidth = sliderBackGroundWidth * 0.8; -const sliderHeight = rectW * 0.15; -const sliderBarWidth = 10; -const sliderBarHeight = rectW * 0.6; -const resetW = 16; -const resetIconD = `M12.3579 13.0447C11.1482 14.0929 9.60059 14.6689 7.99992 14.6667C4.31792 14.6667 1.33325 11.682 1.33325 8.00004C1.33325 4.31804 4.31792 1.33337 7.99992 1.33337C11.6819 1.33337 14.6666 4.31804 14.6666 8.00004C14.6666 9.42404 14.2199 10.744 13.4599 11.8267L11.3333 8.00004H13.3333C13.3332 6.77085 12.9085 5.57942 12.131 4.6273C11.3536 3.67519 10.2712 3.02084 9.06681 2.77495C7.86246 2.52906 6.61014 2.70672 5.5217 3.27788C4.43327 3.84905 3.57553 4.77865 3.0936 5.90943C2.61167 7.04021 2.53512 8.30275 2.87691 9.48347C3.2187 10.6642 3.95785 11.6906 4.96931 12.3891C5.98077 13.0876 7.20245 13.4152 8.42768 13.3166C9.65292 13.218 10.8065 12.6993 11.6933 11.848L12.3579 13.0447Z`; -const pauseIconHeight = rectW * 0.4; -const pauseIconWidth = rectW * 0.08; -const pauseIconGap = rectW * 0.15; -const pauseIconX = (rectW - pauseIconWidth * 2 - pauseIconGap) / 2; -export class TimeControllerView { - constructor(domSelector) { - this.render(domSelector); - this.moveSilderBar = () => {}; - } - play() { - this.renderPauseIcon(); - } - pause() { - this.renderPlayIcon(); - } - renderPlayIcon() { - const playPauseIconG = this.playPauseIconG; - playPauseIconG.selectAll('*').remove(); - playPauseIconG - .append('path') - .attr( - 'd', - `M${rectW * 0.36},${rectW * 0.3}L${rectW * 0.64},${rectW * 0.5}L${ - rectW * 0.36 - },${rectW * 0.7}Z` - ) - .attr('fill', '#000'); - } - renderPauseIcon() { - const playPauseIconG = this.playPauseIconG; - playPauseIconG.selectAll('*').remove(); - playPauseIconG - .selectAll('rect') - .data([, ,]) - .join('rect') - .attr('x', (_, i) => pauseIconX + i * (pauseIconGap + pauseIconWidth)) - .attr('y', (rectW - pauseIconHeight) / 2) - .attr('rx', pauseIconWidth / 2) - .attr('ry', pauseIconWidth / 2) - .attr('width', pauseIconWidth) - .attr('height', pauseIconHeight) - .attr('fill', '#000'); - } - render(domSelector) { - const _dom = d3.select(domSelector); - _dom.selectAll('svg#feder-timer').remove(); - const svg = _dom - .append('svg') - .attr('id', 'feder-timer') - .attr('width', 300) - .attr('height', rectW) - .style('position', 'absolute') - .style('left', '20px') - .style('bottom', '32px'); - // .style('border', '1px solid red'); - - const playPauseG = svg.append('g'); - playPauseG - .append('rect') - .attr('x', 0) - .attr('y', 0) - .attr('width', rectW) - .attr('height', rectW) - .attr('fill', '#fff'); - const playPauseIconG = playPauseG.append('g'); - this.playPauseIconG = playPauseIconG; - this.renderPlayIcon(); - - const sliderG = svg - .append('g') - .attr('transform', `translate(${rectW + iconGap}, 0)`); - sliderG - .append('rect') - .attr('x', 0) - .attr('y', 0) - .attr('width', sliderBackGroundWidth) - .attr('height', rectW) - .attr('fill', '#1D2939'); - sliderG - .append('rect') - .attr('x', sliderBackGroundWidth / 2 - sliderWidth / 2) - .attr('y', rectW / 2 - sliderHeight / 2) - .attr('width', sliderWidth) - .attr('height', sliderHeight) - .attr('fill', '#fff'); - const sliderBar = sliderG - .append('g') - .append('rect') - .datum({ x: 0, y: 0 }) - .attr( - 'transform', - `translate(${ - sliderBackGroundWidth / 2 - sliderWidth / 2 - sliderBarWidth / 2 - },0)` - ) - .attr('x', 0) - .attr('y', rectW / 2 - sliderBarHeight / 2) - .attr('width', sliderBarWidth) - .attr('height', sliderBarHeight) - .attr('fill', '#fff'); - - const resetG = svg - .append('g') - .attr( - 'transform', - `translate(${rectW + iconGap + sliderBackGroundWidth + iconGap}, 0)` - ); - resetG - .append('rect') - .attr('x', 0) - .attr('y', 0) - .attr('width', rectW) - .attr('height', rectW) - .attr('fill', '#fff'); - - resetG - .append('path') - .attr('d', resetIconD) - .attr('fill', '#000') - .attr( - 'transform', - `translate(${rectW / 2 - resetW / 2},${rectW / 2 - resetW / 2})` - ); - - this.playPauseG = playPauseG; - this.sliderBar = sliderBar; - this.resetG = resetG; - } - - setTimer(timer) { - this.playPauseG.on('click', () => timer.playPause()); - this.resetG.on('click', () => timer.restart()); - - const drag = d3 - .drag() - .on('start', () => timer.stop()) - .on('drag', (e, d) => { - const x = Math.max(0, Math.min(e.x, sliderWidth)); - sliderBar.attr('x', (d.x = x)); - timer.setTimeP(x / sliderWidth); - }); - // .on('end', continue); - - const sliderBar = this.sliderBar; - sliderBar.call(drag); - - this.moveSilderBar = (p) => { - const x = p * sliderWidth; - sliderBar.datum().x = x; - sliderBar.attr('x', x); - }; - } -} - -export default TimeControllerView; diff --git a/federjs_old/FederView/HnswView/render/TimerController.js b/federjs_old/FederView/HnswView/render/TimerController.js deleted file mode 100644 index f77c246..0000000 --- a/federjs_old/FederView/HnswView/render/TimerController.js +++ /dev/null @@ -1,82 +0,0 @@ -import * as d3 from 'd3'; - -export default class TimerController { - constructor({ duration, speed = 1, callback, playCallback, pauseCallback }) { - this.callback = callback; - this.speed = speed; - this.duration = duration; - this.playCallback = playCallback; - this.pauseCallback = pauseCallback; - - this.tAlready = 0; - this.t = 0; - this.timer = null; - this.isPlaying = false; - } - get currentT() { - return this.t; - } - start() { - const speed = this.speed; - this.isPlaying || this.playCallback(); - this.isPlaying = true; - this.timer = d3.timer((elapsed) => { - const t = elapsed * speed + this.tAlready; - const p = t / this.duration; - this.t = t; - this.callback({ - t, - p, - }); - if (p >= 1) { - this.stop(); - } - }); - } - restart() { - this.timer.stop(); - this.tAlready = 0; - this.start(); - } - stop() { - this.timer.stop(); - this.tAlready = this.t; - - this.isPlaying && this.pauseCallback(); - this.isPlaying = false; - } - playPause() { - if (this.isPlaying) this.stop(); - else this.start(); - } - continue() { - this.start(); - } - setSpeed(speed) { - this.stop(); - this.speed = speed; - this.continue(); - } - setTimeT(t) { - this.stop(); - const p = t / this.duration; - this.tAlready = t; - this.t = t; - this.callback({ - t, - p, - }); - // this.continue(); - } - setTimeP(p) { - this.stop(); - const t = this.duration * p; - this.tAlready = t; - this.t = t; - this.callback({ - t, - p, - }); - // this.continue(); - } -} diff --git a/federjs_old/FederView/HnswView/render/renderBackground.js b/federjs_old/FederView/HnswView/render/renderBackground.js deleted file mode 100644 index efbb6d3..0000000 --- a/federjs_old/FederView/HnswView/render/renderBackground.js +++ /dev/null @@ -1,11 +0,0 @@ -import { drawRect, blackColor } from 'Utils/renderUtils'; - -export default function renderBackground(ctx, { width, height }) { - drawRect({ - ctx, - width, - height, - hasFill: true, - fillStyle: blackColor, - }); -} diff --git a/federjs_old/FederView/HnswView/render/renderHoverLine.js b/federjs_old/FederView/HnswView/render/renderHoverLine.js deleted file mode 100644 index ab6c100..0000000 --- a/federjs_old/FederView/HnswView/render/renderHoverLine.js +++ /dev/null @@ -1,56 +0,0 @@ -import { drawPath, hexWithOpacity, ZYellow } from 'Utils/renderUtils'; - -const renderHoverLine = ( - ctx, - { hoveredNode, hoveredLevel, clickedNode, clickedLevel }, - { - width, - padding, - hoveredPanelLineWidth, - HoveredPanelLine_1_x, - HoveredPanelLine_1_y, - HoveredPanelLine_2_x, - canvasScale, - } -) => { - let isLeft = true; - let endX = 0; - let endY = 0; - if (!!hoveredNode) { - const [x, y] = hoveredNode.overviewPosLevels[hoveredLevel]; - const originX = (width - padding[1] - padding[3]) / 2 + padding[3]; - isLeft = !clickedNode - ? originX > x - : clickedNode.overviewPosLevels[clickedLevel][0] > x; - const k = isLeft ? -1 : 1; - endX = - x + - HoveredPanelLine_1_x * canvasScale * k + - HoveredPanelLine_2_x * canvasScale * k; - endY = y + HoveredPanelLine_1_y * canvasScale * k; - const points = [ - [x, y], - [ - x + HoveredPanelLine_1_x * canvasScale * k, - y + HoveredPanelLine_1_y * canvasScale * k, - ], - [ - x + - HoveredPanelLine_1_x * canvasScale * k + - HoveredPanelLine_2_x * canvasScale * k, - y + HoveredPanelLine_1_y * canvasScale * k, - ], - ]; - drawPath({ - ctx, - points, - withZ: false, - hasStroke: true, - strokeStyle: hexWithOpacity(ZYellow, 1), - lineWidth: hoveredPanelLineWidth * canvasScale, - }); - } - return { isLeft, endX, endY }; -}; - -export default renderHoverLine; diff --git a/federjs_old/FederView/HnswView/render/renderHoveredPanelLine.js b/federjs_old/FederView/HnswView/render/renderHoveredPanelLine.js deleted file mode 100644 index 3d3abca..0000000 --- a/federjs_old/FederView/HnswView/render/renderHoveredPanelLine.js +++ /dev/null @@ -1,36 +0,0 @@ -import { drawPath, hexWithOpacity, ZYellow } from 'Utils/renderUtils'; - -export default function renderHoveredPanelLine( - ctx, - { x, y, isLeft }, - { - hoveredPanelLineWidth, - HoveredPanelLine_1_x, - HoveredPanelLine_1_y, - HoveredPanelLine_2_x, - canvasScale, - } -) { - const k = isLeft ? -1 : 1; - const points = [ - [x, y], - [ - x + HoveredPanelLine_1_x * canvasScale * k, - y + HoveredPanelLine_1_y * canvasScale * k, - ], - [ - x + - HoveredPanelLine_1_x * canvasScale * k + - HoveredPanelLine_2_x * canvasScale * k, - y + HoveredPanelLine_1_y * canvasScale * k, - ], - ]; - drawPath({ - ctx, - points, - withZ: false, - hasStroke: true, - strokeStyle: hexWithOpacity(ZYellow, 1), - lineWidth: hoveredPanelLineWidth * canvasScale, - }); -} diff --git a/federjs_old/FederView/HnswView/render/renderLinks.js b/federjs_old/FederView/HnswView/render/renderLinks.js deleted file mode 100644 index 024972f..0000000 --- a/federjs_old/FederView/HnswView/render/renderLinks.js +++ /dev/null @@ -1,29 +0,0 @@ -import { - drawLinesWithLinearGradient, - normalGradientStopColors, -} from 'Utils/renderUtils'; -import { shortenLine } from 'Utils'; - -export default function renderLinks( - ctx, - links, - level, - { shortenLineD, overviewLinkLineWidth, canvasScale } -) { - const pointsList = links.map((link) => - shortenLine( - link.source.overviewPosLevels[level], - link.target.overviewPosLevels[level], - shortenLineD * canvasScale - ) - ); - drawLinesWithLinearGradient({ - ctx, - pointsList, - hasStroke: true, - isStrokeLinearGradient: true, - gradientStopColors: normalGradientStopColors, - lineWidth: overviewLinkLineWidth * canvasScale, - lineCap: 'round', - }); -} diff --git a/federjs_old/FederView/HnswView/render/renderNodes.js b/federjs_old/FederView/HnswView/render/renderNodes.js deleted file mode 100644 index fa2c3f7..0000000 --- a/federjs_old/FederView/HnswView/render/renderNodes.js +++ /dev/null @@ -1,21 +0,0 @@ -import { drawEllipse, ZBlue, hexWithOpacity } from 'Utils/renderUtils'; - -export default function renderNodes( - ctx, - nodes, - level, - { canvasScale, ellipseRation, shadowBlur } -) { - drawEllipse({ - ctx, - circles: nodes.map((node) => [ - ...node.overviewPosLevels[level], - node.r * ellipseRation * canvasScale, - node.r * canvasScale, - ]), - hasFill: true, - fillStyle: hexWithOpacity(ZBlue, 0.75), - shadowColor: ZBlue, - shadowBlur: shadowBlur * canvasScale, - }); -} diff --git a/federjs_old/FederView/HnswView/render/renderOverview.js b/federjs_old/FederView/HnswView/render/renderOverview.js deleted file mode 100644 index cbf2a13..0000000 --- a/federjs_old/FederView/HnswView/render/renderOverview.js +++ /dev/null @@ -1,28 +0,0 @@ -import renderBackground from './renderBackground'; -import renderlevelLayer from './renderlevelLayer'; -import renderLinks from './renderLinks'; -import renderNodes from './renderNodes'; -import renderReachable from './renderReachable'; -import renderShortestPath from './renderShortestPath'; - -export default function renderOverview(ctx, federView, overviewHighlightData) { - const { - overviewLevelCount, - overviewNodesLevels, - overviewLinksLevels, - overviewLayerPosLevels, - } = federView; - renderBackground(ctx, federView); - for (let level = 0; level < overviewLevelCount; level++) { - renderlevelLayer(ctx, overviewLayerPosLevels[level], federView); - const nodes = overviewNodesLevels[level]; - const links = overviewLinksLevels[level]; - level > 0 && renderLinks(ctx, links, level, federView); - renderNodes(ctx, nodes, level, federView); - - if (overviewHighlightData) { - renderReachable(ctx, level, overviewHighlightData, federView); - renderShortestPath(ctx, level, overviewHighlightData, federView); - } - } -} diff --git a/federjs_old/FederView/HnswView/render/renderReachable.js b/federjs_old/FederView/HnswView/render/renderReachable.js deleted file mode 100644 index 13c718a..0000000 --- a/federjs_old/FederView/HnswView/render/renderReachable.js +++ /dev/null @@ -1,51 +0,0 @@ -import { - hexWithOpacity, - whiteColor, - drawLinesWithLinearGradient, - neighbourGradientStopColors, - drawEllipse, -} from 'Utils/renderUtils'; - -import { shortenLine } from 'Utils'; - -export default function renderReachableData( - ctx, - level, - { reachableLevel, reachableNodes, reachableLinks }, - { - shortenLineD, - canvasScale, - reachableLineWidth, - ellipseRation, - shadowBlur, - highlightRadiusExt, - posAttr = 'overviewPosLevels', - } -) { - if (level != reachableLevel) return; - const pointsList = reachableLinks - .map((link) => [link.source[posAttr][level], link.target[posAttr][level]]) - .map((points) => shortenLine(...points, shortenLineD * canvasScale)); - drawLinesWithLinearGradient({ - ctx, - pointsList, - hasStroke: true, - isStrokeLinearGradient: true, - gradientStopColors: neighbourGradientStopColors, - lineWidth: reachableLineWidth * canvasScale, - lineCap: 'round', - }); - - drawEllipse({ - ctx, - circles: reachableNodes.map((node) => [ - ...node[posAttr][level], - (node.r + highlightRadiusExt) * ellipseRation * canvasScale, - (node.r + highlightRadiusExt) * canvasScale, - ]), - hasFill: true, - fillStyle: hexWithOpacity(whiteColor, 1), - shadowColor: whiteColor, - shadowBlur: shadowBlur * canvasScale, - }); -} diff --git a/federjs_old/FederView/HnswView/render/renderSearchViewInterLevelLinks.js b/federjs_old/FederView/HnswView/render/renderSearchViewInterLevelLinks.js deleted file mode 100644 index 7181e44..0000000 --- a/federjs_old/FederView/HnswView/render/renderSearchViewInterLevelLinks.js +++ /dev/null @@ -1,92 +0,0 @@ -import { - drawLinesWithLinearGradient, - highLightGradientStopColors, - targetLevelGradientStopColors, -} from 'Utils/renderUtils'; -import { shortenLine, getInprocessPos } from 'Utils'; - -export default function renderSearchViewInterLevelLinks( - ctx, - { entryNodes, inprocessEntryNodes, searchTarget, level }, - { shortenLineD, canvasScale } -) { - const pointsList = entryNodes.map((node) => - shortenLine( - node.searchViewPosLevels[level + 1], - node.searchViewPosLevels[level], - shortenLineD * canvasScale - ) - ); - drawLinesWithLinearGradient({ - ctx, - pointsList, - hasStroke: true, - isStrokeLinearGradient: true, - gradientStopColors: highLightGradientStopColors, - lineWidth: 6, - lineCap: 'round', - }); - const targetPointsList = - pointsList.length === 0 - ? [] - : [ - shortenLine( - searchTarget.searchViewPosLevels[level + 1], - searchTarget.searchViewPosLevels[level], - shortenLineD - ), - ]; - drawLinesWithLinearGradient({ - ctx, - pointsList: targetPointsList, - hasStroke: true, - isStrokeLinearGradient: true, - gradientStopColors: targetLevelGradientStopColors, - lineWidth: 6, - lineCap: 'round', - }); - - const inprocessPointsList = inprocessEntryNodes.map(({ node, t }) => - shortenLine( - node.searchViewPosLevels[level + 1], - getInprocessPos( - node.searchViewPosLevels[level + 1], - node.searchViewPosLevels[level], - t - ), - shortenLineD - ) - ); - drawLinesWithLinearGradient({ - ctx, - pointsList: inprocessPointsList, - hasStroke: true, - isStrokeLinearGradient: true, - gradientStopColors: highLightGradientStopColors, - lineWidth: 6, - lineCap: 'round', - }); - const inprocessTargetPointsList = - inprocessPointsList.length === 0 - ? [] - : [ - shortenLine( - searchTarget.searchViewPosLevels[level + 1], - getInprocessPos( - searchTarget.searchViewPosLevels[level + 1], - searchTarget.searchViewPosLevels[level], - inprocessEntryNodes[0].t - ), - shortenLineD - ), - ]; - drawLinesWithLinearGradient({ - ctx, - pointsList: inprocessTargetPointsList, - hasStroke: true, - isStrokeLinearGradient: true, - gradientStopColors: targetLevelGradientStopColors, - lineWidth: 6, - lineCap: 'round', - }); -} diff --git a/federjs_old/FederView/HnswView/render/renderSearchViewLinks.js b/federjs_old/FederView/HnswView/render/renderSearchViewLinks.js deleted file mode 100644 index fc439ad..0000000 --- a/federjs_old/FederView/HnswView/render/renderSearchViewLinks.js +++ /dev/null @@ -1,184 +0,0 @@ -import { - drawLinesWithLinearGradient, - highLightGradientStopColors, - normalGradientStopColors, -} from 'Utils/renderUtils'; -import { shortenLine, getInprocessPos } from 'Utils'; -import { HNSW_LINK_TYPE } from 'Types'; - -export default function renderSearchViewLinks( - ctx, - { links, inProcessLinks, level }, - { shortenLineD, canvasScale } -) { - let pointsList = []; - let inprocessPointsList = []; - - // Visited - pointsList = links - .filter((link) => link.type === HNSW_LINK_TYPE.Visited) - .map((link) => - shortenLine( - link.source.searchViewPosLevels[level], - link.target.searchViewPosLevels[level], - shortenLineD * canvasScale - ) - ); - drawLinesWithLinearGradient({ - ctx, - pointsList, - hasStroke: true, - isStrokeLinearGradient: true, - gradientStopColors: normalGradientStopColors, - lineWidth: 4, - lineCap: 'round', - }); - inprocessPointsList = inProcessLinks - .filter(({ link }) => link.type === HNSW_LINK_TYPE.Visited) - .map(({ t, link }) => - shortenLine( - link.source.searchViewPosLevels[level], - getInprocessPos( - link.source.searchViewPosLevels[level], - link.target.searchViewPosLevels[level], - t - ), - shortenLineD - ) - ); - drawLinesWithLinearGradient({ - ctx, - pointsList: inprocessPointsList, - hasStroke: true, - isStrokeLinearGradient: true, - gradientStopColors: normalGradientStopColors, - lineWidth: 4, - lineCap: 'round', - }); - - // Extended - pointsList = links - .filter((link) => link.type === HNSW_LINK_TYPE.Extended) - .map((link) => - shortenLine( - link.source.searchViewPosLevels[level], - link.target.searchViewPosLevels[level], - shortenLineD - ) - ); - drawLinesWithLinearGradient({ - ctx, - pointsList, - hasStroke: true, - isStrokeLinearGradient: true, - gradientStopColors: normalGradientStopColors, - lineWidth: 4, - lineCap: 'round', - }); - inprocessPointsList = inProcessLinks - .filter(({ link }) => link.type === HNSW_LINK_TYPE.Extended) - .map(({ t, link }) => - shortenLine( - link.source.searchViewPosLevels[level], - getInprocessPos( - link.source.searchViewPosLevels[level], - link.target.searchViewPosLevels[level], - t - ), - shortenLineD - ) - ); - drawLinesWithLinearGradient({ - ctx, - pointsList: inprocessPointsList, - hasStroke: true, - isStrokeLinearGradient: true, - gradientStopColors: normalGradientStopColors, - lineWidth: 4, - lineCap: 'round', - }); - - // Searched - pointsList = links - .filter((link) => link.type === HNSW_LINK_TYPE.Searched) - .map((link) => - shortenLine( - link.source.searchViewPosLevels[level], - link.target.searchViewPosLevels[level], - shortenLineD - ) - ); - drawLinesWithLinearGradient({ - ctx, - pointsList, - hasStroke: true, - isStrokeLinearGradient: true, - gradientStopColors: highLightGradientStopColors, - lineWidth: 6, - lineCap: 'round', - }); - inprocessPointsList = inProcessLinks - .filter(({ link }) => link.type === HNSW_LINK_TYPE.Searched) - .map(({ t, link }) => - shortenLine( - link.source.searchViewPosLevels[level], - getInprocessPos( - link.source.searchViewPosLevels[level], - link.target.searchViewPosLevels[level], - t - ), - shortenLineD - ) - ); - drawLinesWithLinearGradient({ - ctx, - pointsList: inprocessPointsList, - hasStroke: true, - isStrokeLinearGradient: true, - gradientStopColors: highLightGradientStopColors, - lineWidth: 6, - lineCap: 'round', - }); - - // Fine - pointsList = links - .filter((link) => link.type === HNSW_LINK_TYPE.Fine) - .map((link) => - shortenLine( - link.source.searchViewPosLevels[level], - link.target.searchViewPosLevels[level], - shortenLineD - ) - ); - drawLinesWithLinearGradient({ - ctx, - pointsList, - hasStroke: true, - isStrokeLinearGradient: true, - gradientStopColors: highLightGradientStopColors, - lineWidth: 6, - lineCap: 'round', - }); - inprocessPointsList = inProcessLinks - .filter(({ link }) => link.type === HNSW_LINK_TYPE.Fine) - .map(({ t, link }) => - shortenLine( - link.source.searchViewPosLevels[level], - getInprocessPos( - link.source.searchViewPosLevels[level], - link.target.searchViewPosLevels[level], - t - ), - shortenLineD - ) - ); - drawLinesWithLinearGradient({ - ctx, - pointsList: inprocessPointsList, - hasStroke: true, - isStrokeLinearGradient: true, - gradientStopColors: highLightGradientStopColors, - lineWidth: 6, - lineCap: 'round', - }); -} diff --git a/federjs_old/FederView/HnswView/render/renderSearchViewNodes.js b/federjs_old/FederView/HnswView/render/renderSearchViewNodes.js deleted file mode 100644 index 3123e6e..0000000 --- a/federjs_old/FederView/HnswView/render/renderSearchViewNodes.js +++ /dev/null @@ -1,66 +0,0 @@ -import { - drawEllipse, - hexWithOpacity, - ZBlue, - ZYellow, - ZOrange, - colorScheme, -} from 'Utils/renderUtils'; - -import { HNSW_NODE_TYPE } from 'Types'; - -export default function renderSearchViewNodes( - ctx, - { nodes, level }, - { ellipseRation, shadowBlur } -) { - let _nodes = []; - - // coarse - _nodes = nodes.filter((node) => node.type === HNSW_NODE_TYPE.Coarse); - drawEllipse({ - ctx, - circles: _nodes.map((node) => [ - ...node.searchViewPosLevels[level], - node.r * ellipseRation, - node.r, - ]), - hasFill: true, - fillStyle: hexWithOpacity(ZBlue, 0.7), - shadowColor: ZBlue, - shadowBlur, - }); - - // candidate - _nodes = nodes.filter((node) => node.type === HNSW_NODE_TYPE.Candidate); - drawEllipse({ - ctx, - circles: _nodes.map((node) => [ - ...node.searchViewPosLevels[level], - node.r * ellipseRation, - node.r, - ]), - hasFill: true, - fillStyle: hexWithOpacity(ZYellow, 0.8), - shadowColor: ZYellow, - shadowBlur, - }); - - // fine - _nodes = nodes.filter((node) => node.type === HNSW_NODE_TYPE.Fine); - drawEllipse({ - ctx, - circles: _nodes.map((node) => [ - ...node.searchViewPosLevels[level], - node.r * ellipseRation, - node.r, - ]), - hasFill: true, - fillStyle: hexWithOpacity(colorScheme[2], 1), - hasStroke: true, - lineWidth: 1, - strokeStyle: hexWithOpacity(ZOrange, 0.8), - shadowColor: ZOrange, - shadowBlur, - }); -} diff --git a/federjs_old/FederView/HnswView/render/renderSearchViewTarget.js b/federjs_old/FederView/HnswView/render/renderSearchViewTarget.js deleted file mode 100644 index ccb9c60..0000000 --- a/federjs_old/FederView/HnswView/render/renderSearchViewTarget.js +++ /dev/null @@ -1,18 +0,0 @@ -import { drawEllipse, hexWithOpacity, whiteColor } from 'Utils/renderUtils'; - -export default function renderSearchViewTarget( - ctx, - { node, level }, - { ellipseRation } -) { - drawEllipse({ - ctx, - circles: [ - [...node.searchViewPosLevels[level], node.r * ellipseRation, node.r], - ], - hasFill: true, - fillStyle: hexWithOpacity(whiteColor, 1), - shadowColor: whiteColor, - shadowBlur: 6, - }); -} diff --git a/federjs_old/FederView/HnswView/render/renderSearchViewTransition.js b/federjs_old/FederView/HnswView/render/renderSearchViewTransition.js deleted file mode 100644 index 8a116eb..0000000 --- a/federjs_old/FederView/HnswView/render/renderSearchViewTransition.js +++ /dev/null @@ -1,146 +0,0 @@ -import * as d3 from 'd3'; -import renderBackground from './renderBackground'; -import renderlevelLayer from './renderlevelLayer'; -import renderSearchViewLinks from './renderSearchViewLinks'; -import renderSearchViewInterLevelLinks from './renderSearchViewInterLevelLinks'; -import renderSearchViewNodes from './renderSearchViewNodes'; -import renderSearchViewTarget from './renderSearchViewTarget'; -import renderSelectedNode from './renderSelectedNode'; -import renderHoveredPanelLine from './renderHoveredPanelLine'; -import { - getNodeIdWithLevel, - getLinkIdWithLevel, - getEntryLinkIdWithLevel, -} from 'Utils'; - -export default function renderSearchViewTransition( - ctx, - { - searchNodesLevels, - searchLinksLevels, - searchLayerPosLevels, - searchNodeShowTime, - searchLinkShowTime, - searchTarget, - searchTargetShowTime, - entryNodesLevels, - - clickedLevel, - clickedNode, - hoveredLevel, - hoveredNode, - }, - federView, - { t, p } -) { - const { - searchIntraLevelTime, - searchInterLevelTime, - width, - padding, - canvasScale, - } = federView; - renderBackground(ctx, federView); - - for (let level = 0; level < searchNodesLevels.length; level++) { - renderlevelLayer(ctx, searchLayerPosLevels[level], federView); - const nodes = searchNodesLevels[level].filter( - (node) => searchNodeShowTime[getNodeIdWithLevel(node.id, level)] < t - ); - const links = searchLinksLevels[level].filter( - (link) => - searchLinkShowTime[ - getLinkIdWithLevel(link.source.id, link.target.id, level) - ] + - searchIntraLevelTime < - t - ); - const inProcessLinks = searchLinksLevels[level] - .filter( - (link) => - searchLinkShowTime[ - getLinkIdWithLevel(link.source.id, link.target.id, level) - ] < t && - searchLinkShowTime[ - getLinkIdWithLevel(link.source.id, link.target.id, level) - ] + - searchIntraLevelTime >= - t - ) - .map((link) => ({ - t: - (t - - searchLinkShowTime[ - getLinkIdWithLevel(link.source.id, link.target.id, level) - ]) / - searchIntraLevelTime, - link, - })); - const entryNodes = - level === entryNodesLevels.length - 1 - ? [] - : entryNodesLevels[level].filter( - (entryNode) => - searchLinkShowTime[getEntryLinkIdWithLevel(entryNode.id, level)] + - searchInterLevelTime < - t - ); - const inprocessEntryNodes = - level === entryNodesLevels.length - 1 - ? [] - : entryNodesLevels[level] - .filter( - (entryNode) => - searchLinkShowTime[ - getEntryLinkIdWithLevel(entryNode.id, level) - ] < t && - searchLinkShowTime[ - getEntryLinkIdWithLevel(entryNode.id, level) - ] + - searchInterLevelTime >= - t - ) - .map((node) => ({ - node, - t: - (t - - searchLinkShowTime[getEntryLinkIdWithLevel(node.id, level)]) / - searchInterLevelTime, - })); - renderSearchViewLinks(ctx, { links, inProcessLinks, level }, federView); - // console.log(level, entryNodes); - renderSearchViewInterLevelLinks( - ctx, - { - entryNodes, - inprocessEntryNodes, - searchTarget, - level, - }, - federView - ); - renderSearchViewNodes(ctx, { nodes, level }, federView); - - searchTargetShowTime[level] < t && - renderSearchViewTarget(ctx, { node: searchTarget, level }, federView); - - if (!!hoveredNode) { - const [x, y] = hoveredNode.searchViewPosLevels[hoveredLevel]; - const originX = (width - padding[1] - padding[3]) / 2 + padding[3]; - const isLeft = originX > x; - - renderHoveredPanelLine(ctx, { x, y, isLeft }, federView); - } - - if (!!clickedNode) { - renderSelectedNode( - ctx, - { - pos: clickedNode.searchViewPosLevels[clickedLevel], - r: clickedNode.r + 2 * canvasScale, - }, - federView - ); - } - } -} diff --git a/federjs_old/FederView/HnswView/render/renderSelectedNode.js b/federjs_old/FederView/HnswView/render/renderSelectedNode.js deleted file mode 100644 index 9afcfde..0000000 --- a/federjs_old/FederView/HnswView/render/renderSelectedNode.js +++ /dev/null @@ -1,11 +0,0 @@ -import { drawEllipse, hexWithOpacity, ZYellow } from 'Utils/renderUtils'; - -export default function renderSelectedNode(ctx, { pos, r }, { ellipseRation }) { - drawEllipse({ - ctx, - circles: [[...pos, r * ellipseRation, r]], - hasStroke: true, - strokeStyle: hexWithOpacity(ZYellow, 0.8), - lineWidth: 4, - }); -} diff --git a/federjs_old/FederView/HnswView/render/renderShortestPath.js b/federjs_old/FederView/HnswView/render/renderShortestPath.js deleted file mode 100644 index cd4c292..0000000 --- a/federjs_old/FederView/HnswView/render/renderShortestPath.js +++ /dev/null @@ -1,58 +0,0 @@ -import { shortenLine } from 'Utils'; -import { - drawEllipse, - drawLinesWithLinearGradient, - highLightGradientStopColors, - highLightColor, - hexWithOpacity, -} from 'Utils/renderUtils'; - -export default function renderShortestPath( - ctx, - level, - { SPLinksLevels, SPNodesLevels }, - { - shortenLineD, - canvasScale, - shortestPathLineWidth, - highlightRadiusExt, - shadowBlur, - ellipseRation, - posAttr = 'overviewPosLevels', - } -) { - const pointsList = (SPLinksLevels ? SPLinksLevels[level] : []) - .map((link) => - link.source === link.target - ? !!link.source[posAttr][level + 1] - ? [link.source[posAttr][level + 1], link.target[posAttr][level]] - : null - : [link.source[posAttr][level], link.target[posAttr][level]] - ) - .filter((a) => a) - .map((points) => shortenLine(...points, shortenLineD * canvasScale)); - drawLinesWithLinearGradient({ - ctx, - pointsList, - hasStroke: true, - isStrokeLinearGradient: true, - gradientStopColors: highLightGradientStopColors, - lineWidth: shortestPathLineWidth * canvasScale, - lineCap: 'round', - }); - - drawEllipse({ - ctx, - circles: (SPNodesLevels ? SPNodesLevels[level] : []) - .filter((node) => !!node[posAttr][level]) - .map((node) => [ - ...node[posAttr][level], - (node.r + highlightRadiusExt) * ellipseRation * canvasScale, - (node.r + highlightRadiusExt) * canvasScale, - ]), - hasFill: true, - fillStyle: hexWithOpacity(highLightColor, 1), - shadowColor: highLightColor, - shadowBlur: shadowBlur * canvasScale, - }); -} diff --git a/federjs_old/FederView/HnswView/render/renderlevelLayer.js b/federjs_old/FederView/HnswView/render/renderlevelLayer.js deleted file mode 100644 index 34e7f49..0000000 --- a/federjs_old/FederView/HnswView/render/renderlevelLayer.js +++ /dev/null @@ -1,63 +0,0 @@ -import * as d3 from 'd3'; -import { - drawCircle, - drawPath, - hexWithOpacity, - ZLayerBorder, - whiteColor, - layerGradientStopColors, -} from 'Utils/renderUtils'; -import { dist } from 'Utils'; - -export default function renderlevelLayer( - ctx, - points, - { canvasScale, layerDotNum } -) { - drawPath({ - ctx, - points, - hasStroke: true, - isStrokeLinearGradient: false, - strokeStyle: hexWithOpacity(ZLayerBorder, 0.6), - lineWidth: 0.5 * canvasScale, - hasFill: true, - isFillLinearGradient: true, - gradientStopColors: layerGradientStopColors, - gradientPos: [points[1][0], points[0][1], points[3][0], points[2][1]], - }); - - const rightTopLength = dist(points[0], points[1]); - const leftTopLength = dist(points[0], points[3]); - const rightTopDotNum = layerDotNum; - // const leftTopDotNum = (layerDotNum / rightTopLength) * leftTopLength; - const leftTopDotNum = layerDotNum; - const rightTopVec = [ - points[1][0] - points[0][0], - points[1][1] - points[0][1], - ]; - const leftTopVec = [points[3][0] - points[0][0], points[3][1] - points[0][1]]; - const dots = []; - for (let i = 0; i < layerDotNum; i++) { - const rightTopT = i / layerDotNum + 1 / (2 * layerDotNum); - for (let j = 0; j < layerDotNum; j++) { - const leftTopT = j / layerDotNum + 1 / (2 * layerDotNum); - dots.push([ - points[0][0] + rightTopVec[0] * rightTopT + leftTopVec[0] * leftTopT, - points[0][1] + rightTopVec[1] * rightTopT + leftTopVec[1] * leftTopT, - 0.8 * canvasScale, - ]); - } - } - // d3.range(0.02, 0.98, 1 / rightTopDotNum).forEach((rightTopT) => - // d3.range(0.02, 0.98, 1 / leftTopDotNum).forEach((leftTopT) => { - - // }) - // ); - drawCircle({ - ctx, - circles: dots, - hasFill: true, - fillStyle: hexWithOpacity(whiteColor, 0.4), - }); -} diff --git a/federjs_old/FederView/IvfflatView/InfoPanel/index.js b/federjs_old/FederView/IvfflatView/InfoPanel/index.js deleted file mode 100644 index a795e39..0000000 --- a/federjs_old/FederView/IvfflatView/InfoPanel/index.js +++ /dev/null @@ -1,528 +0,0 @@ -import * as d3 from 'd3'; - -import { - ZYellow, - ZBlue, - whiteColor, - blackColor, - drawPath, - hexWithOpacity, -} from 'Utils/renderUtils'; - -import { showVectors, randomSelect } from 'Utils'; - -export const overviewPanelId = 'feder-info-overview-panel'; -export const hoveredPanelId = 'feder-info-hovered-panel'; - -const panelBackgroundColor = hexWithOpacity(blackColor, 0.6); - -export default class InfoPanel { - constructor({ dom, width, height }) { - this.dom = dom; - this.width = width; - this.height = height; - - this.initOverviewPanel(); - this.initHoveredPanel(); - this.initStyle(); - } - initOverviewPanel() { - const dom = this.dom; - const overviewPanel = document.createElement('div'); - overviewPanel.setAttribute('id', overviewPanelId); - overviewPanel.className = - overviewPanel.className + ' panel-border panel hide'; - const overviewPanelStyle = { - position: 'absolute', - // left: `${canvas.width - 10}px`, - left: '16px', - top: '10px', - width: '240px', - 'max-height': `${this.height - 40}px`, - overflow: 'auto', - borderColor: whiteColor, - backgroundColor: panelBackgroundColor, - // pointerEvents: 'none', - }; - Object.assign(overviewPanel.style, overviewPanelStyle); - dom.appendChild(overviewPanel); - this.overviewPanel = overviewPanel; - } - setOverviewPanelPos(isPanelLeft) { - const overviewPanel = this.overviewPanel; - const overviewPanelStyle = isPanelLeft - ? { - left: '16px', - } - : { - left: null, - right: '16px', - }; - Object.assign(overviewPanel.style, overviewPanelStyle); - } - - updateOverviewOverviewInfo({ indexMeta }) { - const { ntotal, nlist, listSizes } = indexMeta; - const items = [ - { - title: 'IVF_Flat', - }, - { - text: `${ntotal} vectors, divided into ${nlist} clusters.`, - }, - { - text: `The largest cluster has ${d3.max( - listSizes - )} vectors and the smallest cluster has only ${d3.min( - listSizes - )} vectors.`, - }, - ]; - - this.renderOverviewPanel(items, whiteColor); - } - - updateSearchViewCoarseOverviewInfo(searchViewLayoutData, federView) { - const { - nprobe, - clusters, - targetMediaUrl, - nprobeClusters, - switchSearchViewHandlers, - } = searchViewLayoutData; - const { indexMeta } = federView; - const { ntotal, nlist, listSizes } = indexMeta; - const { switchVoronoi, switchPolar, switchProject } = - switchSearchViewHandlers; - const items = [ - { - title: 'IVF_Flat - Search', - }, - { - isImg: true, - imgUrl: targetMediaUrl, - }, - { - isOption: true, - isActive: true, - label: 'Coarse Search', - callback: switchVoronoi, - }, - { - isOption: true, - isActive: false, - label: 'Fine Search (Distance)', - callback: switchPolar, - }, - { - isOption: true, - isActive: false, - label: 'Fine Search (Project)', - callback: switchProject, - }, - { - text: `${ntotal} vectors, divided into ${nlist} clusters.`, - }, - { - title: `Find the ${nprobe} (nprobe=${nprobe}) closest clusters.`, - }, - ...nprobeClusters.map(({ clusterId }) => ({ - text: `cluster-${clusterId} (${ - listSizes[clusterId] - } vectors) dist: ${clusters[clusterId].dis.toFixed(3)}.`, - })), - ]; - - this.renderOverviewPanel(items, whiteColor); - } - - updateSearchViewFinePolarOverviewInfo(searchViewLayoutData, federView) { - const { - k, - nprobe, - nodes, - topKNodes, - switchSearchViewHandlers, - targetMediaUrl, - } = searchViewLayoutData; - const { switchVoronoi, switchPolar, switchProject } = - switchSearchViewHandlers; - const { mediaCallback } = federView; - const fineAllVectorsCount = nodes.length; - const showImages = topKNodes - .map(({ id }) => mediaCallback(id)) - .filter((a) => a); - const items = [ - { - title: 'IVF_Flat - Search', - }, - { - isImg: true, - imgUrl: targetMediaUrl, - }, - { - isOption: true, - isActive: false, - label: 'Coarse Search', - callback: switchVoronoi, - }, - { - isOption: true, - isActive: true, - label: 'Fine Search (Distance)', - callback: switchPolar, - }, - { - isOption: true, - isActive: false, - label: 'Fine Search (Project)', - callback: switchProject, - }, - { - text: `Find the ${k} (k=${k}) vectors closest to the target from these ${nprobe} (nprobe=${nprobe}) clusters, ${fineAllVectorsCount} vectors in total.`, - }, - { - images: showImages, - }, - ]; - - this.renderOverviewPanel(items, whiteColor); - } - - updateSearchViewFineProjectOverviewInfo(searchViewLayoutData, federView) { - const { - nprobe, - nodes, - topKNodes, - targetMediaUrl, - switchSearchViewHandlers, - } = searchViewLayoutData; - const { switchVoronoi, switchPolar, switchProject } = - switchSearchViewHandlers; - const { mediaCallback, viewParams } = federView; - const fineAllVectorsCount = nodes.length; - const showImages = topKNodes - .map(({ id }) => mediaCallback(id)) - .filter((a) => a); - const items = [ - { - title: 'IVF_Flat - Search', - }, - { - isImg: true, - imgUrl: targetMediaUrl, - }, - { - isOption: true, - isActive: false, - label: 'Coarse Search', - callback: switchVoronoi, - }, - { - isOption: true, - isActive: false, - label: 'Fine Search (Distance)', - callback: switchPolar, - }, - { - isOption: true, - isActive: true, - label: 'Fine Search (Project)', - callback: switchProject, - }, - { - text: `Projection of all ${fineAllVectorsCount} vectors in the ${nprobe} (nprobe=${nprobe}) clusters using ${ - viewParams.projectMethod || 'random' - }.`, - }, - { - images: showImages, - }, - ]; - - this.renderOverviewPanel(items, whiteColor); - } - - initHoveredPanel() { - const dom = this.dom; - const hoveredPanel = document.createElement('div'); - hoveredPanel.setAttribute('id', hoveredPanelId); - hoveredPanel.className = hoveredPanel.className + 'panel-border panel hide'; - const hoveredPanelStyle = { - position: 'absolute', - left: 0, - // right: '30px', - top: 0, - width: '200px', - // display: 'flex', - pointerEvents: 'none', - backgroundColor: panelBackgroundColor, - }; - Object.assign(hoveredPanel.style, hoveredPanelStyle); - dom.appendChild(hoveredPanel); - } - - updateOverviewHoveredInfo({ - hoveredCluster = null, - listIds = [], - images = [], - x = 0, - y = 0, - } = {}) { - const showImages = randomSelect(images, 9).filter((a) => a); - // console.log('showImages', showImages); - const items = hoveredCluster - ? [ - { - title: `cluster-${hoveredCluster.clusterId}`, - }, - { - text: `including ${listIds.length} vectors`, - }, - { - images: showImages, - }, - ] - : []; - - this.renderHoveredPanel(items, ZYellow, x, y); - } - - updateSearchViewHoveredInfo({ - hoveredCluster = null, - listIds = [], - images = [], - x = 0, - y = 0, - } = {}) { - if (!hoveredCluster) { - this.renderHoveredPanel(); - } else { - const showImages = randomSelect( - images.filter((a) => a), - 9 - ); - // console.log('showImages', showImages, images); - const items = hoveredCluster - ? [ - { - title: `cluster-${hoveredCluster.clusterId}`, - }, - { - text: `including ${listIds.length} vectors`, - }, - { - text: `distance from center: ${hoveredCluster.dis.toFixed(3)}`, - }, - { - images: showImages, - }, - ] - : []; - - this.renderHoveredPanel(items, ZYellow, x, y); - } - } - - updateSearchViewHoveredNodeInfo({ - hoveredNode = null, - img = '', - x = 0, - y = 0, - } = {}) { - if (!hoveredNode) { - this.renderHoveredPanel(); - } else { - const items = hoveredNode - ? [ - { - title: `Row No. ${hoveredNode.id}`, - }, - { - text: `belong to cluster-${hoveredNode.listId}`, - }, - { - text: `distance the target: ${hoveredNode.dis.toFixed(3)}`, - }, - { - isImg: true, - imgUrl: img, - }, - ] - : []; - - this.renderHoveredPanel(items, ZYellow, x, y); - } - } - - initStyle() { - const style = document.createElement('style'); - style.type = 'text/css'; - style.innerHTML = ` - .panel-border { - border-style: dashed; - border-width: 1px; - } - .panel { - padding: 6px 8px; - font-size: 12px; - } - .hide { - opacity: 0; - } - .panel-item { - margin-bottom: 6px; - } - .panel-img { - width: 150px; - height: 100px; - background-size: cover; - margin-bottom: 12px; - border-radius: 4px; - border: 1px solid ${ZYellow}; - } - .panel-item-display-flex { - display: flex; - } - .panel-item-title { - font-weight: 600; - margin-bottom: 3px; - } - .panel-item-text { - font-weight: 400; - font-size: 10px; - word-break: break-all; - } - .panel-item-text-flex { - margin-left: 8px; - } - .panel-item-text-margin { - margin: 0 6px; - } - .text-no-wrap { - white-space: nowrap; - } - .panel-img-gallery { - display: grid; - grid-template-columns: repeat(3, 1fr); - grid-row-gap: 8px; - grid-column-gap: 8px; - // flex-wrap: wrap; - } - .panel-img-gallery-item { - width: 100%; - height: 44px; - background-size: cover; - border-radius: 2px; - // border: 1px solid ${ZYellow}; - } - .panel-item-option { - display: flex; - align-items: center; - cursor: pointer; - // pointer-events: auto; - } - .panel-item-option-icon { - width: 6px; - height: 6px; - border-radius: 7px; - border: 2px solid ${ZYellow}; - margin-left: 2px; - } - .panel-item-option-icon-active { - background-color: ${ZYellow}; - } - .panel-item-option-label { - margin-left: 6px; - } - `; - document.getElementsByTagName('head').item(0).appendChild(style); - } - renderHoveredPanel(itemList = [], color = '#000', x = 0, y = 0) { - const panel = d3.select(this.dom).select(`#${hoveredPanelId}`); - if (itemList.length === 0) panel.classed('hide', true); - else { - panel.style('color', color); - // panel.style.left = x + 'px'; - // panel.style.top = y + 'px'; - const isLeft = x > this.width * 0.7; - if (isLeft) { - panel.style('left', null); - panel.style('right', this.width - x - 10 + 'px'); - } else { - panel.style('left', x + 10 + 'px'); - panel.style('right', null); - } - - const isTop = y > this.height * 0.7; - if (isTop) { - panel.style('top', null); - panel.style('bottom', this.height - y - 6 + 'px'); - } else { - panel.style('top', y + 6 + 'px'); - panel.style('bottom', null); - } - - this.renderPanel(panel, itemList); - } - } - renderOverviewPanel(itemList = [], color) { - const panel = d3.select(this.dom).select(`#${overviewPanelId}`); - panel.style('color', color); - if (itemList.length === 0) panel.classed('hide', true); - else { - this.renderPanel(panel, itemList); - } - } - renderPanel(panel, itemList) { - panel.classed('hide', false); - panel.selectAll('*').remove(); - - itemList.forEach((item) => { - const div = panel.append('div'); - div.classed('panel-item', true); - item.isFlex && div.classed('panel-item-display-flex', true); - if (item.isImg && item.imgUrl) { - div.classed('panel-img', true); - div.style('background-image', `url(${item.imgUrl})`); - } - if (item.title) { - const title = div.append('div'); - title.classed('panel-item-title', true); - title.text(item.title); - } - if (item.text) { - const title = div.append('div'); - title.classed('panel-item-text', true); - item.isFlex && title.classed('panel-item-text-flex', true); - item.textWithMargin && title.classed('panel-item-text-margin', true); - item.noWrap && title.classed('text-no-wrap', true); - title.text(item.text); - } - if (item.images) { - const imagesDiv = div.append('div'); - imagesDiv.classed('panel-img-gallery', true); - item.images.forEach((url) => { - const imgItem = imagesDiv.append('div'); - imgItem.classed('panel-img-gallery-item', true); - imgItem.style('background-image', `url(${url})`); - }); - } - if (item.isOption) { - const optionDiv = div.append('div'); - optionDiv.classed('panel-item-option', true); - const optionIcon = optionDiv.append('div'); - optionIcon.classed('panel-item-option-icon', true); - if (item.isActive) - optionIcon.classed('panel-item-option-icon-active', true); - else optionIcon.classed('panel-item-option-icon-active', false); - - const optionLabel = optionDiv.append('div'); - optionLabel.classed('panel-item-option-label', true); - optionLabel.text(item.label); - - item.callback && optionDiv.on('click', item.callback); - } - }); - } -} diff --git a/federjs_old/FederView/IvfflatView/index.js b/federjs_old/FederView/IvfflatView/index.js deleted file mode 100644 index 09cd9a8..0000000 --- a/federjs_old/FederView/IvfflatView/index.js +++ /dev/null @@ -1,378 +0,0 @@ -import * as d3 from 'd3'; -import BaseView from '../BaseView.js'; -import overviewLayoutHandler from './layout/overviewLayout'; -import renderVoronoiView from './render/renderVoronoiView'; -import renderNodeView from './render/renderNodeView'; -import mouse2voronoi from './layout/mouse2voronoi'; -import mouse2node from './layout/mouse2node'; -import searchViewLayoutHandler from './layout/searchViewLayout'; -import { SEARCH_VIEW_TYPE, VIEW_TYPE } from 'Types.js'; -import animateCoarse2Fine from './render/animateCoarse2Fine'; -import animateFine2Coarse from './render/animateFine2Coarse'; -import animateFine2Fine from './render/animateFine2Fine'; -import InfoPanel from './InfoPanel'; - -const defaultIvfflatViewParams = { - minVoronoiRadius: 4, - // voronoiForceTime: 3000, - // nodeCollisionForceTime: 1000, - backgroundColor: 'red', - voronoiStrokeWidth: 2, - targetNodeStrokeWidth: 5, - targetNodeR: 7, - topKNodeR: 5, - hoveredNodeR: 6, - hoveredNodeStrokeWidth: 1, - hoveredNodeOpacity: 1, - topKNodeOpacity: 0.7, - topKNodeStrokeWidth: 1, - nonTopKNodeR: 3, - nonTopKNodeOpacity: 0.4, - projectPadding: [20, 20, 20, 260], - axisTickCount: 5, - polarAxisStrokeWidth: 1, - polarAxisOpacity: 0.4, - polarOriginBias: 0.15, - ease: d3.easeCubic, - animateExitTime: 1500, - animateEnterTime: 1000, - fineSearchNodeTransTime: 1200, - forceIterations: 100, -}; - -export default class IvfflatView extends BaseView { - constructor({ indexMeta, viewParams, getVectorById }) { - super({ - viewParams, - getVectorById, - }); - for (let key in defaultIvfflatViewParams) { - this[key] = - key in viewParams ? viewParams[key] : defaultIvfflatViewParams[key]; - } - - this.projectPadding = this.projectPadding.map( - (num) => num * this.canvasScale - ); - this.indexMeta = indexMeta; - this.overviewLayoutHandler(); - } - initInfoPanel(dom) { - const infoPanel = new InfoPanel({ - dom, - width: this.clientWidth, - height: this.clientHeight, - }); - return infoPanel; - } - overviewLayoutHandler() { - this.overviewLayoutPromise = overviewLayoutHandler(this).then( - ({ clusters, voronoi }) => { - // this.clusters = clusters; - // this.OVVoronoi = voronoi; - this.overviewLayoutData = { clusters, OVVoronoi: voronoi }; - } - ); - } - renderOverview(ctx, infoPanel) { - infoPanel.updateOverviewOverviewInfo(this); - renderVoronoiView(ctx, VIEW_TYPE.overview, this.overviewLayoutData, this); - } - getOverviewEventHandler(ctx, infoPanel) { - let hoveredClusterId = null; - - const mouseLeaveHandler = () => { - hoveredClusterId = null; - renderVoronoiView(ctx, VIEW_TYPE.overview, this.overviewLayoutData, this); - infoPanel.updateOverviewHoveredInfo(); - }; - - const mouseMoveHandler = ({ x, y }) => { - const currentHoveredClusterId = mouse2voronoi({ - voronoi: this.overviewLayoutData.OVVoronoi, - x, - y, - }); - - if (hoveredClusterId !== currentHoveredClusterId) { - hoveredClusterId = currentHoveredClusterId; - const hoveredCluster = this.overviewLayoutData.clusters.find( - (cluster) => cluster.clusterId == hoveredClusterId - ); - if (!!hoveredCluster) { - renderVoronoiView( - ctx, - VIEW_TYPE.overview, - this.overviewLayoutData, - this, - hoveredCluster - ); - infoPanel.updateOverviewHoveredInfo({ - hoveredCluster, - listIds: this.indexMeta.listIds[hoveredClusterId], - images: this.mediaCallback - ? this.indexMeta.listIds[hoveredClusterId].map((listId) => - this.mediaCallback(listId) - ) - : [], - x: hoveredCluster.OVPolyCentroid[0] / this.canvasScale, - y: hoveredCluster.OVPolyCentroid[1] / this.canvasScale, - }); - } - } - }; - - return { mouseLeaveHandler, mouseMoveHandler }; - } - - async searchViewHandler(searchRes) { - this.overviewLayoutPromise && (await this.overviewLayoutPromise); - - const searchViewLayoutData = { - nprobe: searchRes.csResIds.length, - k: searchRes.fsResIds.length, - clusters: JSON.parse(JSON.stringify(this.overviewLayoutData.clusters)), - }; - searchViewLayoutData.nprobeClusters = searchViewLayoutData.clusters.filter( - (cluster) => searchRes.csResIds.indexOf(cluster.clusterId) >= 0 - ); - searchViewLayoutData.nonNprobeClusters = - searchViewLayoutData.clusters.filter( - (cluster) => searchRes.csResIds.indexOf(cluster.clusterId) < 0 - ); - searchViewLayoutData.colorScheme = d3 - .range(searchViewLayoutData.nprobe) - .map((i) => - d3.hsl((360 * i) / searchViewLayoutData.nprobe, 1, 0.5).formatHex() - ); - await searchViewLayoutHandler(searchRes, searchViewLayoutData, this); - return searchViewLayoutData; - } - renderSearchView(ctx, infoPanel, searchViewLayoutData, targetMediaUrl) { - searchViewLayoutData.targetMediaUrl = targetMediaUrl; - searchViewLayoutData.switchSearchViewHandlers = { - switchVoronoi: () => - this.switchSearchView( - SEARCH_VIEW_TYPE.voronoi, - ctx, - infoPanel, - searchViewLayoutData - ), - switchPolar: () => - this.switchSearchView( - SEARCH_VIEW_TYPE.polar, - ctx, - infoPanel, - searchViewLayoutData - ), - switchProject: () => - this.switchSearchView( - SEARCH_VIEW_TYPE.project, - ctx, - infoPanel, - searchViewLayoutData - ), - }; - searchViewLayoutData.searchViewType = SEARCH_VIEW_TYPE.voronoi; - this.updateCoarseSearchInfoPanel(infoPanel, searchViewLayoutData); - this.renderCoarseSearch(ctx, searchViewLayoutData); - } - renderCoarseSearch(ctx, searchViewLayoutData) { - renderVoronoiView(ctx, VIEW_TYPE.search, searchViewLayoutData, this); - } - updateCoarseSearchInfoPanel(infoPanel, searchViewLayoutData) { - infoPanel.setOverviewPanelPos( - !searchViewLayoutData.targetNode.isLeft_coarseLevel - ); - infoPanel.updateSearchViewCoarseOverviewInfo(searchViewLayoutData, this); - } - renderFineSearch( - ctx, - searchViewLayoutData, - searchViewType = SEARCH_VIEW_TYPE.polar, - hoveredNode - ) { - renderNodeView( - ctx, - searchViewLayoutData, - this, - searchViewType, - hoveredNode - ); - } - updateFineSearchInfoPanel( - infoPanel, - searchViewLayoutData, - searchViewType = SEARCH_VIEW_TYPE.polar - ) { - searchViewType === SEARCH_VIEW_TYPE.polar && - infoPanel.updateSearchViewFinePolarOverviewInfo( - searchViewLayoutData, - this - ); - searchViewType === SEARCH_VIEW_TYPE.project && - infoPanel.updateSearchViewFineProjectOverviewInfo( - searchViewLayoutData, - this - ); - } - switchSearchView(searchViewType, ctx, infoPanel, searchViewLayoutData) { - if (searchViewType == searchViewLayoutData.searchViewType) return; - - const oldSearchViewType = searchViewLayoutData.searchViewType; - const newSearchViewType = searchViewType; - - // coarse => fine - - if (oldSearchViewType === SEARCH_VIEW_TYPE.voronoi) { - // console.log('coarse => fine [start]'); - this.updateFineSearchInfoPanel( - infoPanel, - searchViewLayoutData, - newSearchViewType - ); - const endCallback = () => { - searchViewLayoutData.searchViewType = newSearchViewType; - this.renderFineSearch(ctx, searchViewLayoutData, newSearchViewType); - }; - animateCoarse2Fine( - oldSearchViewType, - newSearchViewType, - ctx, - searchViewLayoutData, - this, - endCallback - ); - } - - // fine => coarse - if (newSearchViewType === SEARCH_VIEW_TYPE.voronoi) { - // console.log('fine => coarse [start]'); - this.updateCoarseSearchInfoPanel(infoPanel, searchViewLayoutData); - const endCallback = () => { - searchViewLayoutData.searchViewType = newSearchViewType; - this.renderCoarseSearch(ctx, searchViewLayoutData); - }; - animateFine2Coarse( - oldSearchViewType, - newSearchViewType, - ctx, - searchViewLayoutData, - this, - endCallback - ); - } - - // fine - intra - if ( - newSearchViewType !== SEARCH_VIEW_TYPE.voronoi && - oldSearchViewType !== SEARCH_VIEW_TYPE.voronoi - ) { - this.updateFineSearchInfoPanel( - infoPanel, - searchViewLayoutData, - newSearchViewType - ); - // console.log('fine - intra [start]'); - const endCallback = () => { - searchViewLayoutData.searchViewType = newSearchViewType; - this.renderFineSearch(ctx, searchViewLayoutData, newSearchViewType); - }; - animateFine2Fine( - oldSearchViewType, - newSearchViewType, - ctx, - searchViewLayoutData, - this, - endCallback - ); - } - } - getSearchViewEventHandler(ctx, searchViewLayoutData, infoPanel) { - let hoveredClusterId = null; - let hoveredNode = null; - const mouseLeaveHandler = () => { - hoveredClusterId = null; - hoveredNode = null; - const { searchViewType } = searchViewLayoutData; - if (searchViewType === SEARCH_VIEW_TYPE.voronoi) { - renderVoronoiView(ctx, VIEW_TYPE.search, searchViewLayoutData, this); - infoPanel.updateSearchViewHoveredInfo(); - } else { - infoPanel.updateSearchViewHoveredNodeInfo(); - } - }; - const mouseMoveHandler = ({ x, y }) => { - const { searchViewType, clusters, nodes } = searchViewLayoutData; - if (searchViewType === SEARCH_VIEW_TYPE.voronoi) { - const currentHoveredClusterId = mouse2voronoi({ - voronoi: searchViewLayoutData.SVVoronoi, - x, - y, - }); - if (hoveredClusterId !== currentHoveredClusterId) { - hoveredClusterId = currentHoveredClusterId; - const hoveredCluster = clusters.find( - (cluster) => cluster.clusterId == hoveredClusterId - ); - renderVoronoiView( - ctx, - VIEW_TYPE.search, - searchViewLayoutData, - this, - hoveredCluster - ); - - if (!!hoveredCluster) { - infoPanel.updateSearchViewHoveredInfo({ - hoveredCluster, - listIds: this.indexMeta.listIds[hoveredClusterId], - images: this.mediaCallback - ? this.indexMeta.listIds[hoveredClusterId].map((listId) => - this.mediaCallback(listId) - ) - : [], - x: hoveredCluster.SVPolyCentroid[0] / this.canvasScale, - y: hoveredCluster.SVPolyCentroid[1] / this.canvasScale, - }); - } - } - } else { - if (!nodes) return; - const nodesPos = - searchViewType === SEARCH_VIEW_TYPE.polar - ? nodes.map((node) => node.polarPos) - : nodes.map((node) => node.projectPos); - const hoveredNodeIndex = mouse2node({ - nodesPos, - x, - y, - bias: (this.hoveredNodeR + 2) * this.canvasScale, - }); - const currentHoveredNode = - hoveredNodeIndex >= 0 ? nodes[hoveredNodeIndex] : null; - if (hoveredNode !== currentHoveredNode) { - hoveredNode = currentHoveredNode; - this.renderFineSearch( - ctx, - searchViewLayoutData, - searchViewType, - hoveredNode - ); - } - - const img = - hoveredNode && this.mediaCallback - ? this.mediaCallback(hoveredNode.id) - : ''; - infoPanel.updateSearchViewHoveredNodeInfo({ - hoveredNode, - img, - x: x / this.canvasScale, - y: y / this.canvasScale, - }); - } - }; - return { mouseLeaveHandler, mouseMoveHandler }; - } -} diff --git a/federjs_old/FederView/IvfflatView/layout/SVCoarseVoronoiHandler.js b/federjs_old/FederView/IvfflatView/layout/SVCoarseVoronoiHandler.js deleted file mode 100644 index 931673e..0000000 --- a/federjs_old/FederView/IvfflatView/layout/SVCoarseVoronoiHandler.js +++ /dev/null @@ -1,153 +0,0 @@ -import * as d3 from 'd3'; -import getVoronoi from './getVoronoi'; -import { vecSort } from 'Utils'; - -export default function SVCoarseVoronoiHandler( - searchRes, - searchViewLayoutData, - federView -) { - return new Promise((resolve) => { - const { clusters, nprobeClusters, nprobe } = searchViewLayoutData; - const { width, height, forceIterations, polarOriginBias } = federView; - const fineClusterOrder = vecSort( - nprobeClusters, - 'OVPolyCentroid', - 'clusterId' - ); - // console.log('fineClusterOrder', fineClusterOrder); - - const targetClusterId = searchRes.coarse[0].id; - const targetCluster = clusters.find( - (cluster) => cluster.clusterId === targetClusterId - ); - const otherFineClustersId = fineClusterOrder.filter( - (clusterId) => clusterId !== targetClusterId - ); - const links = otherFineClustersId.map((clusterId) => ({ - source: clusterId, - target: targetClusterId, - })); - clusters.forEach((cluster) => { - cluster.x = cluster.forceProjection[0]; - cluster.y = cluster.forceProjection[1]; - }); - const targetClusterX = - nprobeClusters.reduce((acc, cluster) => acc + cluster.x, 0) / nprobe; - const targetClusterY = - nprobeClusters.reduce((acc, cluster) => acc + cluster.y, 0) / nprobe; - targetCluster.x = targetClusterX; - targetCluster.y = targetClusterY; - - const otherFineCluster = otherFineClustersId.map((clusterId) => - nprobeClusters.find((cluster) => cluster.clusterId === clusterId) - ); - const angleStep = (2 * Math.PI) / (nprobe - 1); - const biasR = targetCluster.r * 0.5; - otherFineCluster.forEach((cluster, i) => { - cluster.x = targetClusterX + biasR * Math.sin(angleStep * i); - cluster.y = targetClusterY + biasR * Math.cos(angleStep * i); - }); - - const simulation = d3 - .forceSimulation(clusters) - .alphaDecay(1 - Math.pow(0.001, 1 / forceIterations)) - .force( - 'links', - d3 - .forceLink(links) - .id((cluster) => cluster.clusterId) - .strength((_) => 0.25) - ) - .force( - 'collision', - d3 - .forceCollide() - .radius((cluster) => cluster.r) - .strength(0.1) - ) - .force('center', d3.forceCenter(width / 2, height / 2)) - .on('tick', () => { - // border - clusters.forEach((cluster) => { - cluster.x = Math.max( - cluster.r, - Math.min(width - cluster.r, cluster.x) - ); - cluster.y = Math.max( - cluster.r, - Math.min(height - cluster.r, cluster.y) - ); - }); - }) - .on('end', () => { - clusters.forEach((cluster) => { - cluster.SVPos = [cluster.x, cluster.y]; - }); - const voronoi = getVoronoi(clusters, width, height); - clusters.forEach((cluster, i) => { - const points = voronoi.cellPolygon(i); - points.pop(); - cluster.SVPolyPoints = points; - cluster.SVPolyCentroid = d3.polygonCentroid(points); - }); - searchViewLayoutData.SVVoronoi = voronoi; - - const targetCluster = clusters.find( - (cluster) => cluster.clusterId === targetClusterId - ); - const centoid_fineClusters_x = - nprobeClusters.reduce( - (acc, cluster) => acc + cluster.SVPolyCentroid[0], - 0 - ) / nprobe; - const centroid_fineClusters_y = - nprobeClusters.reduce( - (acc, cluster) => acc + cluster.SVPolyCentroid[1], - 0 - ) / nprobe; - const _x = centoid_fineClusters_x - targetCluster.SVPos[0]; - const _y = centroid_fineClusters_y - targetCluster.SVPos[1]; - const biasR = Math.sqrt(_x * _x + _y * _y); - const targetNode = { - SVPos: [ - targetCluster.SVPos[0] + targetCluster.r * 0.4 * (_x / biasR), - targetCluster.SVPos[1] + targetCluster.r * 0.4 * (_y / biasR), - ], - }; - targetNode.isLeft_coarseLevel = targetNode.SVPos[0] < width / 2; - searchViewLayoutData.targetNode = targetNode; - - const polarOrigin = [ - width / 2 + - (targetNode.isLeft_coarseLevel ? -1 : 1) * polarOriginBias * width, - height / 2, - ]; - // const polarOrigin = [width / 2, height / 2]; - searchViewLayoutData.polarOrigin = polarOrigin; - targetNode.polarPos = polarOrigin; - const polarMaxR = Math.min(width, height) * 0.5 - 5; - searchViewLayoutData.polarMaxR = polarMaxR; - const angleStep = (Math.PI * 2) / fineClusterOrder.length; - nprobeClusters.forEach((cluster) => { - const order = fineClusterOrder.indexOf(cluster.clusterId); - cluster.polarOrder = order; - cluster.SVNextLevelPos = [ - polarOrigin[0] + (polarMaxR / 2) * Math.sin(angleStep * order), - polarOrigin[1] + (polarMaxR / 2) * Math.cos(angleStep * order), - ]; - cluster.SVNextLevelTran = [ - cluster.SVNextLevelPos[0] - cluster.SVPolyCentroid[0], - cluster.SVNextLevelPos[1] - cluster.SVPolyCentroid[1], - ]; - }); - const clusterId2cluster = {}; - nprobeClusters.forEach((cluster) => { - clusterId2cluster[cluster.clusterId] = cluster; - }); - searchViewLayoutData.clusterId2cluster = clusterId2cluster; - - resolve(); - }); - }); -} diff --git a/federjs_old/FederView/IvfflatView/layout/SVFinePolarHandler.js b/federjs_old/FederView/IvfflatView/layout/SVFinePolarHandler.js deleted file mode 100644 index 154f9cd..0000000 --- a/federjs_old/FederView/IvfflatView/layout/SVFinePolarHandler.js +++ /dev/null @@ -1,67 +0,0 @@ -import * as d3 from 'd3'; - -export default function SVFinePolarHandler( - searchRes, - searchViewLayoutData, - federView -) { - return new Promise((resolve) => { - const { polarMaxR, polarOrigin, clusterId2cluster } = searchViewLayoutData; - const { forceIterations, nonTopKNodeR, canvasScale } = federView; - const nodes = searchRes.fine; - - const distances = nodes - .map((node) => node.dis) - .filter((a) => a > 0) - .sort(); - const minDis = distances.length > 0 ? distances[0] : 0; - const maxDis = - distances.length > 0 - ? distances[Math.round((distances.length - 1) * 0.98)] - : 0; - const r = d3 - .scaleLinear() - .domain([minDis, maxDis]) - .range([polarMaxR * 0.2, polarMaxR]) - .clamp(true); - - nodes.forEach((node) => { - const cluster = clusterId2cluster[node.listId]; - const { polarOrder, SVNextLevelPos } = cluster; - node.polarOrder = polarOrder; - const randAngle = Math.random() * Math.PI * 2; - const randBias = [Math.sin, Math.cos].map( - (f) => cluster.r * Math.random() * 0.7 * f(randAngle) - ); - node.voronoiPos = SVNextLevelPos.map((d, i) => d + randBias[i]); - node.x = node.voronoiPos[0]; - node.y = node.voronoiPos[1]; - node.r = r(node.dis); - }); - - const simulation = d3 - .forceSimulation(nodes) - .alphaDecay(1 - Math.pow(0.001, (1 / forceIterations) * 2)) - .force( - 'collide', - d3 - .forceCollide() - .radius((_) => nonTopKNodeR * canvasScale) - .strength(0.4) - ) - .force('r', d3.forceRadial((node) => node.r, ...polarOrigin).strength(1)) - .on('end', () => { - nodes.forEach((node) => { - node.polarPos = [node.x, node.y]; - }); - searchViewLayoutData.nodes = nodes; - searchViewLayoutData.topKNodes = nodes.filter((node) => - searchRes.fsResIds.find((id) => id == node.id) - ); - searchViewLayoutData.nonTopKNodes = nodes.filter( - (node) => !searchRes.fsResIds.find((id) => id == node.id) - ); - resolve(); - }); - }); -} diff --git a/federjs_old/FederView/IvfflatView/layout/SVFineProjectHandler.js b/federjs_old/FederView/IvfflatView/layout/SVFineProjectHandler.js deleted file mode 100644 index fc1b200..0000000 --- a/federjs_old/FederView/IvfflatView/layout/SVFineProjectHandler.js +++ /dev/null @@ -1,30 +0,0 @@ -import * as d3 from 'd3'; - -export default function SVFineProjectHandler( - searchRes, - searchViewLayoutData, - federView -) { - const { nodes, targetNode } = searchViewLayoutData; - const { projectPadding, width, height } = federView; - if (!nodes[0].projection) { - console.log('No Projection Data. Should use "fineWithProjection".'); - nodes.forEach((node) => { - node.projection = [Math.random(), Math.random()]; - }); - } - const xRange = targetNode.isLeft_coarseLevel - ? [projectPadding[1], width - projectPadding[3]] - : [projectPadding[3], width - projectPadding[1]]; - const x = d3 - .scaleLinear() - .domain(d3.extent(nodes, (node) => node.projection[0])) - .range(xRange); - const y = d3 - .scaleLinear() - .domain(d3.extent(nodes, (node) => node.projection[1])) - .range([projectPadding[0], height - projectPadding[2]]); - nodes.forEach((node) => { - node.projectPos = [x(node.projection[0]), y(node.projection[1])]; - }); -} diff --git a/federjs_old/FederView/IvfflatView/layout/getVoronoi.js b/federjs_old/FederView/IvfflatView/layout/getVoronoi.js deleted file mode 100644 index c9e0ba4..0000000 --- a/federjs_old/FederView/IvfflatView/layout/getVoronoi.js +++ /dev/null @@ -1,9 +0,0 @@ -import * as d3 from 'd3'; - -export default function getVoronoi(clusters, width, height) { - const delaunay = d3.Delaunay.from( - clusters.map((cluster) => [cluster.x, cluster.y]) - ); - const voronoi = delaunay.voronoi([0, 0, width, height]); - return voronoi; -} diff --git a/federjs_old/FederView/IvfflatView/layout/mouse2node.js b/federjs_old/FederView/IvfflatView/layout/mouse2node.js deleted file mode 100644 index 19ce021..0000000 --- a/federjs_old/FederView/IvfflatView/layout/mouse2node.js +++ /dev/null @@ -1,8 +0,0 @@ -import { dist2 } from 'Utils'; -import * as d3 from 'd3'; - -export default function mouse2node({ nodesPos, x, y, bias }) { - const minIndex = d3.minIndex(nodesPos, (nodePos) => dist2(nodePos, [x, y])); - - return dist2(nodesPos[minIndex], [x, y]) > Math.pow(bias, 2) ? -1 : minIndex; -} diff --git a/federjs_old/FederView/IvfflatView/layout/mouse2voronoi.js b/federjs_old/FederView/IvfflatView/layout/mouse2voronoi.js deleted file mode 100644 index ba6636b..0000000 --- a/federjs_old/FederView/IvfflatView/layout/mouse2voronoi.js +++ /dev/null @@ -1,3 +0,0 @@ -export default function mouse2node({ voronoi, x, y }) { - return voronoi.delaunay.find(x, y); -} \ No newline at end of file diff --git a/federjs_old/FederView/IvfflatView/layout/overviewLayout.js b/federjs_old/FederView/IvfflatView/layout/overviewLayout.js deleted file mode 100644 index 6862e16..0000000 --- a/federjs_old/FederView/IvfflatView/layout/overviewLayout.js +++ /dev/null @@ -1,78 +0,0 @@ -import * as d3 from 'd3'; -import getVoronoi from './getVoronoi'; - -export default function overviewLayoutHandler({ - indexMeta, - width, - height, - canvasScale, - minVoronoiRadius, - forceIterations, -}) { - return new Promise((resolve) => { - const allArea = width * height; - const { ntotal, listCentroidProjections = null, listSizes } = indexMeta; - const clusters = listSizes.map((listSize, i) => ({ - clusterId: i, - oriProjection: listCentroidProjections - ? listCentroidProjections[i] - : [Math.random(), Math.random()], - count: listSize, - countP: listSize / ntotal, - countArea: allArea * (listSize / ntotal), - })); - - const x = d3 - .scaleLinear() - .domain(d3.extent(clusters, (cluster) => cluster.oriProjection[0])) - .range([0, width]); - const y = d3 - .scaleLinear() - .domain(d3.extent(clusters, (cluster) => cluster.oriProjection[1])) - .range([0, height]); - - clusters.forEach((cluster) => { - cluster.x = x(cluster.oriProjection[0]); - cluster.y = y(cluster.oriProjection[1]); - cluster.r = Math.max( - minVoronoiRadius * canvasScale, - Math.sqrt(cluster.countArea / Math.PI) - ); - }); - - const simulation = d3 - .forceSimulation(clusters) - .alphaDecay(1 - Math.pow(0.001, 1 / forceIterations)) - .force( - 'collision', - d3.forceCollide().radius((cluster) => cluster.r) - ) - .force('center', d3.forceCenter(width / 2, height / 2)) - .on('tick', () => { - // border - clusters.forEach((cluster) => { - cluster.x = Math.max( - cluster.r, - Math.min(width - cluster.r, cluster.x) - ); - cluster.y = Math.max( - cluster.r, - Math.min(height - cluster.r, cluster.y) - ); - }); - }) - .on('end', () => { - clusters.forEach((cluster) => { - cluster.forceProjection = [cluster.x, cluster.y]; - }); - const voronoi = getVoronoi(clusters, width, height); - clusters.forEach((cluster, i) => { - const points = voronoi.cellPolygon(i); - points.pop(); - cluster.OVPolyPoints = points; - cluster.OVPolyCentroid = d3.polygonCentroid(points); - }); - resolve({ clusters, voronoi }); - }); - }); -} diff --git a/federjs_old/FederView/IvfflatView/layout/searchViewLayout.js b/federjs_old/FederView/IvfflatView/layout/searchViewLayout.js deleted file mode 100644 index e0e072d..0000000 --- a/federjs_old/FederView/IvfflatView/layout/searchViewLayout.js +++ /dev/null @@ -1,20 +0,0 @@ -import SVCoarseVoronoiHandler from './SVCoarseVoronoiHandler'; -import SVFinePolarHandler from './SVFinePolarHandler'; -import SVFineProjectHandler from './SVFineProjectHandler'; - -export default function searchViewLayoutHandler( - searchRes, - searchViewLayoutData, - federView -) { - return new Promise(async (resolve) => { - searchRes.coarse.forEach( - ({ id, dis }) => (searchViewLayoutData.clusters[id].dis = dis) - ); - await SVCoarseVoronoiHandler(searchRes, searchViewLayoutData, federView); - await SVFinePolarHandler(searchRes, searchViewLayoutData, federView); - SVFineProjectHandler(searchRes, searchViewLayoutData, federView); - // console.log('searchViewLayoutData', searchViewLayoutData); - resolve(); - }); -} diff --git a/federjs_old/FederView/IvfflatView/render/animateCoarse2Fine.js b/federjs_old/FederView/IvfflatView/render/animateCoarse2Fine.js deleted file mode 100644 index 1a540e5..0000000 --- a/federjs_old/FederView/IvfflatView/render/animateCoarse2Fine.js +++ /dev/null @@ -1,82 +0,0 @@ -import * as d3 from 'd3'; -import renderBackground from './renderBackground'; -import animateNonNprobeClusters from './animateNonNprobeClusters'; -import animateNprobeClustersTrans from './animateNprobeClustersTrans'; -import animateTargetNode from './animateTargetNode'; -import animateNprobeClustersOpacity from './animateNprobeClustersOpacity'; -import animateNodesOpacityAndTrans from './animateNodesOpacityAndTrans'; -import { ANIMATION_TYPE } from 'Types'; - -export default function animateCoarse2Fine( - oldSearchViewType, - newSearchViewType, - ctx, - searchViewLayoutData, - federView, - endCallback = () => {} -) { - const { animateExitTime, animateEnterTime } = federView; - const stepAllTime = animateExitTime + animateEnterTime; - const timer = d3.timer((elapsed) => { - renderBackground(ctx, federView); - - animateNonNprobeClusters({ - ctx, - searchViewLayoutData, - federView, - elapsed, - delay: 0, - duration: animateExitTime, - animationType: ANIMATION_TYPE.exit, - }); - - animateNprobeClustersTrans({ - ctx, - searchViewLayoutData, - federView, - elapsed, - delay: 0, - duration: animateExitTime, - animationType: ANIMATION_TYPE.exit, - }); - - animateTargetNode({ - ctx, - searchViewLayoutData, - federView, - elapsed, - delay: 0, - duration: animateExitTime, - animationType: ANIMATION_TYPE.exit, - newSearchViewType, - }); - - animateNprobeClustersOpacity({ - ctx, - searchViewLayoutData, - federView, - elapsed, - delay: animateExitTime, - duration: animateEnterTime, - animationType: ANIMATION_TYPE.exit, - }); - - animateNodesOpacityAndTrans({ - ctx, - searchViewLayoutData, - federView, - elapsed, - delay: animateExitTime, - duration: animateEnterTime, - animationType: ANIMATION_TYPE.enter, - oldSearchViewType, - newSearchViewType, - }); - - if (elapsed >= stepAllTime) { - console.log('Coarse => Fine [OK]'); - timer.stop(); - endCallback(); - } - }); -} diff --git a/federjs_old/FederView/IvfflatView/render/animateFine2Coarse.js b/federjs_old/FederView/IvfflatView/render/animateFine2Coarse.js deleted file mode 100644 index 11fd978..0000000 --- a/federjs_old/FederView/IvfflatView/render/animateFine2Coarse.js +++ /dev/null @@ -1,83 +0,0 @@ -import * as d3 from 'd3'; -import renderBackground from './renderBackground'; -import animateNonNprobeClusters from './animateNonNprobeClusters'; -import animateNprobeClustersTrans from './animateNprobeClustersTrans'; -import animateTargetNode from './animateTargetNode'; -import animateNprobeClustersOpacity from './animateNprobeClustersOpacity'; -import animateNodesOpacityAndTrans from './animateNodesOpacityAndTrans'; -import { ANIMATION_TYPE } from 'Types'; - -export default function animateFine2Coarse( - oldSearchViewType, - newSearchViewType, - ctx, - searchViewLayoutData, - federView, - endCallback = () => {} -) { - const { animateExitTime, animateEnterTime } = federView; - - const stepAllTime = animateExitTime + animateEnterTime; - const timer = d3.timer((elapsed) => { - renderBackground(ctx, federView); - - animateNodesOpacityAndTrans({ - ctx, - searchViewLayoutData, - federView, - elapsed, - delay: 0, - duration: animateExitTime, - animationType: ANIMATION_TYPE.exit, - oldSearchViewType, - newSearchViewType, - }); - - animateNprobeClustersOpacity({ - ctx, - searchViewLayoutData, - federView, - elapsed, - delay: 0, - duration: animateExitTime, - animationType: ANIMATION_TYPE.enter, - }); - - animateNonNprobeClusters({ - ctx, - searchViewLayoutData, - federView, - elapsed, - delay: animateExitTime, - duration: animateEnterTime, - animationType: ANIMATION_TYPE.enter, - }); - - animateNprobeClustersTrans({ - ctx, - searchViewLayoutData, - federView, - elapsed, - delay: animateExitTime, - duration: animateEnterTime, - animationType: ANIMATION_TYPE.enter, - }); - - animateTargetNode({ - ctx, - searchViewLayoutData, - federView, - elapsed, - delay: animateExitTime, - duration: animateEnterTime, - animationType: ANIMATION_TYPE.enter, - newSearchViewType, - }); - - if (elapsed >= stepAllTime) { - console.log('Fine => Coarse [OK]'); - timer.stop(); - endCallback(); - } - }); -} diff --git a/federjs_old/FederView/IvfflatView/render/animateFine2Fine.js b/federjs_old/FederView/IvfflatView/render/animateFine2Fine.js deleted file mode 100644 index 347d7a4..0000000 --- a/federjs_old/FederView/IvfflatView/render/animateFine2Fine.js +++ /dev/null @@ -1,32 +0,0 @@ -import renderBackground from './renderBackground'; -import animateNodesTrans from './animateNodesTrans'; -import * as d3 from 'd3'; - -export default function animateFine2Fine( - oldSearchViewType, - newSearchViewType, - ctx, - searchViewLayoutData, - federView, - endCallback -) { - const { fineSearchNodeTransTime } = federView; - const timer = d3.timer((elapsed) => { - renderBackground(ctx, federView); - animateNodesTrans({ - ctx, - searchViewLayoutData, - federView, - elapsed, - duration: fineSearchNodeTransTime, - delay: 0, - newSearchViewType, - }); - - if (elapsed >= fineSearchNodeTransTime) { - console.log(`${oldSearchViewType} To ${newSearchViewType} OK!`); - timer.stop(); - endCallback(); - } - }); -} diff --git a/federjs_old/FederView/IvfflatView/render/animateNodesOpacityAndTrans.js b/federjs_old/FederView/IvfflatView/render/animateNodesOpacityAndTrans.js deleted file mode 100644 index 58461f2..0000000 --- a/federjs_old/FederView/IvfflatView/render/animateNodesOpacityAndTrans.js +++ /dev/null @@ -1,85 +0,0 @@ -import { drawCircle, hexWithOpacity, whiteColor } from 'Utils/renderUtils'; -import { ANIMATION_TYPE, SEARCH_VIEW_TYPE } from 'Types'; - -export default function animateNodesOpacityAndTrans({ - ctx, - searchViewLayoutData, - federView, - elapsed, - duration, - delay, - animationType, - newSearchViewType, - oldSearchViewType, -}) { - const { colorScheme, nprobe, topKNodes, nonTopKNodes } = searchViewLayoutData; - const { - ease, - topKNodeR, - topKNodeOpacity, - topKNodeStrokeWidth, - nonTopKNodeR, - nonTopKNodeOpacity, - canvasScale, - } = federView; - let t = ease((elapsed - delay) / duration); - if (t > 1 || t < 0) return; - t = animationType === ANIMATION_TYPE.enter ? 1 - t : t; - - const nonTopKCircles = - (newSearchViewType === SEARCH_VIEW_TYPE.polar && - animationType === ANIMATION_TYPE.enter) || - (oldSearchViewType === SEARCH_VIEW_TYPE.polar && - animationType === ANIMATION_TYPE.exit) - ? nonTopKNodes.map((node) => [ - t * node.voronoiPos[0] + (1 - t) * node.polarPos[0], - t * node.voronoiPos[1] + (1 - t) * node.polarPos[1], - nonTopKNodeR * canvasScale, - node.polarOrder, - ]) - : nonTopKNodes.map((node) => [ - t * node.voronoiPos[0] + (1 - t) * node.projectPos[0], - t * node.voronoiPos[1] + (1 - t) * node.projectPos[1], - nonTopKNodeR * canvasScale, - node.polarOrder, - ]); - const topKCircles = - (newSearchViewType === SEARCH_VIEW_TYPE.polar && - animationType === ANIMATION_TYPE.enter) || - (oldSearchViewType === SEARCH_VIEW_TYPE.polar && - animationType === ANIMATION_TYPE.exit) - ? topKNodes.map((node) => [ - t * node.voronoiPos[0] + (1 - t) * node.polarPos[0], - t * node.voronoiPos[1] + (1 - t) * node.polarPos[1], - topKNodeR * canvasScale, - node.polarOrder, - ]) - : topKNodes.map((node) => [ - t * node.voronoiPos[0] + (1 - t) * node.projectPos[0], - t * node.voronoiPos[1] + (1 - t) * node.projectPos[1], - topKNodeR * canvasScale, - node.polarOrder, - ]); - for (let i = 0; i < nprobe; i++) { - let circles = nonTopKCircles.filter((circle) => circle[3] == i); - drawCircle({ - ctx, - circles, - hasFill: true, - fillStyle: hexWithOpacity(colorScheme[i], nonTopKNodeOpacity), - }); - } - const opacity = t * 0.5 + (1 - t) * topKNodeOpacity; - for (let i = 0; i < nprobe; i++) { - let circles = topKCircles.filter((circle) => circle[3] == i); - drawCircle({ - ctx, - circles, - hasFill: true, - fillStyle: hexWithOpacity(colorScheme[i], opacity), - hasStroke: true, - strokeStyle: hexWithOpacity(whiteColor, opacity), - lineWidth: topKNodeStrokeWidth * canvasScale, - }); - } -} diff --git a/federjs_old/FederView/IvfflatView/render/animateNodesTrans.js b/federjs_old/FederView/IvfflatView/render/animateNodesTrans.js deleted file mode 100644 index a5e98b0..0000000 --- a/federjs_old/FederView/IvfflatView/render/animateNodesTrans.js +++ /dev/null @@ -1,61 +0,0 @@ -import { SEARCH_VIEW_TYPE } from 'Types'; -import { hexWithOpacity, drawCircle, whiteColor } from 'Utils/renderUtils'; - -export default function animateNodesTrans({ - ctx, - searchViewLayoutData, - federView, - elapsed, - duration, - delay, - newSearchViewType, -}) { - const { colorScheme, nonTopKNodes, topKNodes, nprobe } = searchViewLayoutData; - const { - ease, - nonTopKNodeR, - canvasScale, - topKNodeR, - topKNodeStrokeWidth, - nonTopKNodeOpacity, - topKNodeOpacity, - } = federView; - let t = ease((elapsed - delay) / duration); - if (t > 1 || t < 0) return; - - t = newSearchViewType === SEARCH_VIEW_TYPE.polar ? 1 - t : t; - - const nonTopKCircles = nonTopKNodes.map((node) => [ - t * node.projectPos[0] + (1 - t) * node.polarPos[0], - t * node.projectPos[1] + (1 - t) * node.polarPos[1], - nonTopKNodeR * canvasScale, - node.polarOrder, - ]); - const topKCircles = topKNodes.map((node) => [ - t * node.projectPos[0] + (1 - t) * node.polarPos[0], - t * node.projectPos[1] + (1 - t) * node.polarPos[1], - topKNodeR * canvasScale, - node.polarOrder, - ]); - for (let i = 0; i < nprobe; i++) { - let circles = nonTopKCircles.filter((circle) => circle[3] == i); - drawCircle({ - ctx, - circles, - hasFill: true, - fillStyle: hexWithOpacity(colorScheme[i], nonTopKNodeOpacity), - }); - } - for (let i = 0; i < nprobe; i++) { - let circles = topKCircles.filter((circle) => circle[3] == i); - drawCircle({ - ctx, - circles, - hasFill: true, - fillStyle: hexWithOpacity(colorScheme[i], topKNodeOpacity), - hasStroke: true, - strokeStyle: hexWithOpacity(whiteColor, topKNodeOpacity), - lineWidth: topKNodeStrokeWidth * canvasScale, - }); - } -} diff --git a/federjs_old/FederView/IvfflatView/render/animateNonNprobeClusters.js b/federjs_old/FederView/IvfflatView/render/animateNonNprobeClusters.js deleted file mode 100644 index 7a56657..0000000 --- a/federjs_old/FederView/IvfflatView/render/animateNonNprobeClusters.js +++ /dev/null @@ -1,33 +0,0 @@ -import { - drawVoronoi, - hexWithOpacity, - blackColor, - ZBlue, -} from 'Utils/renderUtils'; -import { ANIMATION_TYPE } from 'Types'; - -export default function animateNonNprobeClusters({ - ctx, - elapsed, - duration, - delay, - animationType, - searchViewLayoutData, - federView, -}) { - const { ease, voronoiStrokeWidth, canvasScale } = federView; - const { nonNprobeClusters } = searchViewLayoutData; - let t = ease((elapsed - delay) / duration); - if (t > 1 || t < 0) return; - const opacity = animationType === ANIMATION_TYPE.enter ? t : 1 - t; - const pointsList = nonNprobeClusters.map((cluster) => cluster.SVPolyPoints); - drawVoronoi({ - ctx, - pointsList, - hasStroke: true, - strokeStyle: blackColor, - lineWidth: voronoiStrokeWidth * canvasScale, - hasFill: true, - fillStyle: hexWithOpacity(ZBlue, opacity), - }); -} diff --git a/federjs_old/FederView/IvfflatView/render/animateNprobeClustersOpacity.js b/federjs_old/FederView/IvfflatView/render/animateNprobeClustersOpacity.js deleted file mode 100644 index ab4ded6..0000000 --- a/federjs_old/FederView/IvfflatView/render/animateNprobeClustersOpacity.js +++ /dev/null @@ -1,39 +0,0 @@ -import { - drawVoronoi, - hexWithOpacity, - blackColor, - ZLightBlue, -} from 'Utils/renderUtils'; -import { ANIMATION_TYPE } from 'Types'; - -export default function animateNonNprobeClusters({ - ctx, - searchViewLayoutData, - federView, - elapsed, - duration, - delay, - animationType, -}) { - const { nprobeClusters } = searchViewLayoutData; - const { ease, voronoiStrokeWidth, canvasScale } = federView; - let t = ease((elapsed - delay) / duration); - if (t > 1 || t < 0) return; - t = animationType === ANIMATION_TYPE.enter ? t : 1 - t; - const opacity = t; - const pointsList = nprobeClusters.map((cluster) => - cluster.SVPolyPoints.map((point) => [ - point[0] + cluster.SVNextLevelTran[0], - point[1] + cluster.SVNextLevelTran[1], - ]) - ); - drawVoronoi({ - ctx, - pointsList, - hasStroke: true, - strokeStyle: blackColor, - lineWidth: voronoiStrokeWidth * canvasScale, - hasFill: true, - fillStyle: hexWithOpacity(ZLightBlue, opacity), - }); -} diff --git a/federjs_old/FederView/IvfflatView/render/animateNprobeClustersTrans.js b/federjs_old/FederView/IvfflatView/render/animateNprobeClustersTrans.js deleted file mode 100644 index c0adcb6..0000000 --- a/federjs_old/FederView/IvfflatView/render/animateNprobeClustersTrans.js +++ /dev/null @@ -1,40 +0,0 @@ -import { - drawVoronoi, - hexWithOpacity, - whiteColor, - blackColor, - ZLightBlue, -} from 'Utils/renderUtils'; -import { ANIMATION_TYPE } from 'Types'; - -export default function animateNprobeClustersTrans({ - ctx, - searchViewLayoutData, - federView, - elapsed, - duration, - delay, - animationType, -}) { - const { nprobeClusters } = searchViewLayoutData; - const { ease, voronoiStrokeWidth, canvasScale } = federView; - let t = ease((elapsed - delay) / duration); - if (t > 1 || t < 0) return; - t = animationType === ANIMATION_TYPE.enter ? 1 - t : t; - - const pointsList = nprobeClusters.map((cluster) => - cluster.SVPolyPoints.map((point) => [ - point[0] + t * cluster.SVNextLevelTran[0], - point[1] + t * cluster.SVNextLevelTran[1], - ]) - ); - drawVoronoi({ - ctx, - pointsList, - hasStroke: true, - strokeStyle: blackColor, - lineWidth: voronoiStrokeWidth * canvasScale, - hasFill: true, - fillStyle: hexWithOpacity(ZLightBlue, 1), - }); -} diff --git a/federjs_old/FederView/IvfflatView/render/animateTargetNode.js b/federjs_old/FederView/IvfflatView/render/animateTargetNode.js deleted file mode 100644 index 3ba0e20..0000000 --- a/federjs_old/FederView/IvfflatView/render/animateTargetNode.js +++ /dev/null @@ -1,33 +0,0 @@ -import { whiteColor, drawCircle } from 'Utils/renderUtils'; -import { ANIMATION_TYPE, SEARCH_VIEW_TYPE } from 'Types'; - -export default function animateTargetNode({ - ctx, - searchViewLayoutData, - federView, - elapsed, - duration, - delay, - animationType, - newSearchViewType, -}) { - const { targetNode } = searchViewLayoutData; - const { ease, targetNodeR, canvasScale, targetNodeStrokeWidth } = federView; - let t = ease((elapsed - delay) / duration); - if (newSearchViewType === SEARCH_VIEW_TYPE.project) { - if (t < 0 || t > 1) return; - } - if (t > 1) t = 1; - if (t < 0) t = 0; - t = animationType === ANIMATION_TYPE.enter ? t : 1 - t; - const x = targetNode.SVPos[0] * t + targetNode.polarPos[0] * (1 - t); - const y = targetNode.SVPos[1] * t + targetNode.polarPos[1] * (1 - t); - - drawCircle({ - ctx, - circles: [[x, y, targetNodeR * canvasScale]], - hasStroke: true, - strokeStyle: whiteColor, - lineWidth: targetNodeStrokeWidth * canvasScale, - }); -} diff --git a/federjs_old/FederView/IvfflatView/render/renderBackground.js b/federjs_old/FederView/IvfflatView/render/renderBackground.js deleted file mode 100644 index e1cd225..0000000 --- a/federjs_old/FederView/IvfflatView/render/renderBackground.js +++ /dev/null @@ -1,14 +0,0 @@ -import { drawRect, blackColor } from 'Utils/renderUtils'; - -export default function renderBackground(ctx, { - width, - height, -}) { - drawRect({ - ctx, - width, - height, - hasFill: true, - fillStyle: blackColor, - }); -} diff --git a/federjs_old/FederView/IvfflatView/render/renderHighLightNodes.js b/federjs_old/FederView/IvfflatView/render/renderHighLightNodes.js deleted file mode 100644 index 216e270..0000000 --- a/federjs_old/FederView/IvfflatView/render/renderHighLightNodes.js +++ /dev/null @@ -1,34 +0,0 @@ -import { SEARCH_VIEW_TYPE } from 'Types'; -import { hexWithOpacity, drawCircle, whiteColor } from 'Utils/renderUtils'; - -export default function renderHighLightNodes( - ctx, - { colorScheme, topKNodes, nprobe }, - { topKNodeR, canvasScale, topKNodeOpacity, topKNodeStrokeWidth }, - searchViewType -) { - const allCircles = - searchViewType === SEARCH_VIEW_TYPE.polar - ? topKNodes.map((node) => [ - ...node.polarPos, - topKNodeR * canvasScale, - node.polarOrder, - ]) - : topKNodes.map((node) => [ - ...node.projectPos, - topKNodeR * canvasScale, - node.polarOrder, - ]); - for (let i = 0; i < nprobe; i++) { - let circles = allCircles.filter((circle) => circle[3] == i); - drawCircle({ - ctx, - circles, - hasFill: true, - fillStyle: hexWithOpacity(colorScheme[i], topKNodeOpacity), - hasStroke: true, - strokeStyle: hexWithOpacity(whiteColor, topKNodeOpacity), - lineWidth: topKNodeStrokeWidth * canvasScale, - }); - } -} diff --git a/federjs_old/FederView/IvfflatView/render/renderHighlightVoronoi.js b/federjs_old/FederView/IvfflatView/render/renderHighlightVoronoi.js deleted file mode 100644 index 0d9330c..0000000 --- a/federjs_old/FederView/IvfflatView/render/renderHighlightVoronoi.js +++ /dev/null @@ -1,25 +0,0 @@ -import { VIEW_TYPE } from 'Types'; -import { - drawVoronoi, - ZYellow, - ZLightBlue, - hexWithOpacity, - blackColor, -} from 'Utils/renderUtils'; - -export default function renderHighlightVoronoi( - ctx, - { nprobeClusters }, - { voronoiStrokeWidth, canvasScale } -) { - const pointsList = nprobeClusters.map((cluster) => cluster.SVPolyPoints); - drawVoronoi({ - ctx, - pointsList, - hasStroke: true, - strokeStyle: blackColor, - lineWidth: voronoiStrokeWidth * canvasScale, - hasFill: true, - fillStyle: hexWithOpacity(ZLightBlue, 1), - }); -} diff --git a/federjs_old/FederView/IvfflatView/render/renderNodeView.js b/federjs_old/FederView/IvfflatView/render/renderNodeView.js deleted file mode 100644 index 89123e9..0000000 --- a/federjs_old/FederView/IvfflatView/render/renderNodeView.js +++ /dev/null @@ -1,41 +0,0 @@ -import renderBackground from './renderBackground'; -import renderPolarAxis from './renderPolarAxis'; -import renderNormalNodes from './renderNormalNodes'; -import renderHighLightNodes from './renderHighLightNodes'; -import renderSelectedNode from './renderSelectedNode'; -import renderTarget from './renderTarget'; -import { SEARCH_VIEW_TYPE } from 'Types'; - -export default function renderNodeView( - ctx, - searchViewLayoutData, - federView, - searchViewType = SEARCH_VIEW_TYPE.polar, - hoveredNode = null -) { - // background - renderBackground(ctx, federView); - - // axis polar - searchViewType === SEARCH_VIEW_TYPE.polar && - renderPolarAxis(ctx, searchViewLayoutData, federView); - - // normal-nodes - renderNormalNodes(ctx, searchViewLayoutData, federView, searchViewType); - - // topk-nodes - renderHighLightNodes(ctx, searchViewLayoutData, federView, searchViewType); - - // hovered node - !!hoveredNode && - renderSelectedNode( - ctx, - searchViewLayoutData, - federView, - searchViewType, - hoveredNode - ); - - // target - renderTarget(ctx, searchViewType, searchViewLayoutData, federView); -} diff --git a/federjs_old/FederView/IvfflatView/render/renderNormalNodes.js b/federjs_old/FederView/IvfflatView/render/renderNormalNodes.js deleted file mode 100644 index d136c49..0000000 --- a/federjs_old/FederView/IvfflatView/render/renderNormalNodes.js +++ /dev/null @@ -1,31 +0,0 @@ -import { SEARCH_VIEW_TYPE } from 'Types'; -import { hexWithOpacity, drawCircle } from 'Utils/renderUtils'; - -export default function renderNormalNodes( - ctx, - { colorScheme, nonTopKNodes, nprobe }, - { nonTopKNodeR, canvasScale, nonTopKNodeOpacity }, - searchViewType -) { - const allCircles = - searchViewType === SEARCH_VIEW_TYPE.polar - ? nonTopKNodes.map((node) => [ - ...node.polarPos, - nonTopKNodeR * canvasScale, - node.polarOrder, - ]) - : nonTopKNodes.map((node) => [ - ...node.projectPos, - nonTopKNodeR * canvasScale, - node.polarOrder, - ]); - for (let i = 0; i < nprobe; i++) { - let circles = allCircles.filter((circle) => circle[3] == i); - drawCircle({ - ctx, - circles, - hasFill: true, - fillStyle: hexWithOpacity(colorScheme[i], nonTopKNodeOpacity), - }); - } -} diff --git a/federjs_old/FederView/IvfflatView/render/renderNormalVoronoi.js b/federjs_old/FederView/IvfflatView/render/renderNormalVoronoi.js deleted file mode 100644 index 296d88c..0000000 --- a/federjs_old/FederView/IvfflatView/render/renderNormalVoronoi.js +++ /dev/null @@ -1,28 +0,0 @@ -import { VIEW_TYPE } from 'Types'; -import { - drawVoronoi, - ZBlue, - hexWithOpacity, - blackColor, -} from 'Utils/renderUtils'; - -export default function renderNormalVoronoi( - ctx, - viewType, - { clusters, nonNprobeClusters }, - { voronoiStrokeWidth, canvasScale } -) { - const pointsList = - viewType === VIEW_TYPE.overview - ? clusters.map((cluster) => cluster.OVPolyPoints) - : nonNprobeClusters.map((cluster) => cluster.SVPolyPoints); - drawVoronoi({ - ctx, - pointsList, - hasStroke: true, - strokeStyle: blackColor, - lineWidth: voronoiStrokeWidth * canvasScale, - hasFill: true, - fillStyle: hexWithOpacity(ZBlue, 1), - }); -} diff --git a/federjs_old/FederView/IvfflatView/render/renderPolarAxis.js b/federjs_old/FederView/IvfflatView/render/renderPolarAxis.js deleted file mode 100644 index bcc4135..0000000 --- a/federjs_old/FederView/IvfflatView/render/renderPolarAxis.js +++ /dev/null @@ -1,19 +0,0 @@ -import { hexWithOpacity, drawCircle, ZBlue } from 'Utils/renderUtils'; -import * as d3 from 'd3'; - -export default function renderPolarAxis( - ctx, - { polarOrigin, polarMaxR }, - { axisTickCount, polarAxisStrokeWidth, canvasScale, polarAxisOpacity } -) { - const circles = d3 - .range(axisTickCount) - .map((i) => [...polarOrigin, ((i + 0.7) / axisTickCount) * polarMaxR]); - drawCircle({ - ctx, - circles, - hasStroke: true, - lineWidth: polarAxisStrokeWidth * canvasScale, - strokeStyle: hexWithOpacity(ZBlue, polarAxisOpacity), - }); -} diff --git a/federjs_old/FederView/IvfflatView/render/renderSelectedNode.js b/federjs_old/FederView/IvfflatView/render/renderSelectedNode.js deleted file mode 100644 index f774f0f..0000000 --- a/federjs_old/FederView/IvfflatView/render/renderSelectedNode.js +++ /dev/null @@ -1,32 +0,0 @@ -import { SEARCH_VIEW_TYPE } from 'Types'; -import { hexWithOpacity, drawCircle, whiteColor } from 'Utils/renderUtils'; - -export default function renderSelectedNode( - ctx, - { colorScheme }, - { hoveredNodeR, canvasScale, hoveredNodeOpacity, hoveredNodeStrokeWidth }, - searchViewType, - hoveredNode -) { - const circle = - searchViewType === SEARCH_VIEW_TYPE.polar - ? [ - ...hoveredNode.polarPos, - hoveredNodeR * canvasScale, - hoveredNode.polarOrder, - ] - : [ - ...hoveredNode.projectPos, - hoveredNodeR * canvasScale, - hoveredNode.polarOrder, - ]; - drawCircle({ - ctx, - circles: [circle], - hasFill: true, - fillStyle: hexWithOpacity(colorScheme[circle[3]], hoveredNodeOpacity), - hasStroke: true, - lineWidth: hoveredNodeStrokeWidth * canvasScale, - strokeStyle: hexWithOpacity(whiteColor, 1), - }); -} diff --git a/federjs_old/FederView/IvfflatView/render/renderSelectedVoronoi.js b/federjs_old/FederView/IvfflatView/render/renderSelectedVoronoi.js deleted file mode 100644 index 71165e6..0000000 --- a/federjs_old/FederView/IvfflatView/render/renderSelectedVoronoi.js +++ /dev/null @@ -1,27 +0,0 @@ -import { VIEW_TYPE } from 'Types'; -import { - drawVoronoi, - ZLightBlue, - ZYellow, - hexWithOpacity, - blackColor, -} from 'Utils/renderUtils'; - -export default function renderNormalVoronoi(ctx, viewType, hoveredCluster, { - voronoiStrokeWidth, - canvasScale, -}) { - const pointsList = - viewType === VIEW_TYPE.overview - ? [hoveredCluster.OVPolyPoints] - : [hoveredCluster.SVPolyPoints]; - drawVoronoi({ - ctx, - pointsList, - hasStroke: true, - strokeStyle: blackColor, - lineWidth: voronoiStrokeWidth * canvasScale, - hasFill: true, - fillStyle: hexWithOpacity(ZYellow, 0.8), - }); -} diff --git a/federjs_old/FederView/IvfflatView/render/renderTarget.js b/federjs_old/FederView/IvfflatView/render/renderTarget.js deleted file mode 100644 index 6309889..0000000 --- a/federjs_old/FederView/IvfflatView/render/renderTarget.js +++ /dev/null @@ -1,22 +0,0 @@ -import { SEARCH_VIEW_TYPE } from 'Types'; -import { whiteColor, drawCircle } from 'Utils/renderUtils'; - -export default function renderTarget( - ctx, - searchViewType, - { targetNode }, - { targetNodeR, canvasScale, targetNodeStrokeWidth } -) { - if (searchViewType === SEARCH_VIEW_TYPE.project) return; - const circle = - searchViewType === SEARCH_VIEW_TYPE.voronoi - ? [...targetNode.SVPos, targetNodeR * canvasScale] - : [...targetNode.polarPos, targetNodeR * canvasScale]; - drawCircle({ - ctx, - circles: [circle], - hasStroke: true, - strokeStyle: whiteColor, - lineWidth: targetNodeStrokeWidth * canvasScale, - }); -} diff --git a/federjs_old/FederView/IvfflatView/render/renderVoronoiView.js b/federjs_old/FederView/IvfflatView/render/renderVoronoiView.js deleted file mode 100644 index d62ee2f..0000000 --- a/federjs_old/FederView/IvfflatView/render/renderVoronoiView.js +++ /dev/null @@ -1,31 +0,0 @@ -import renderBackground from './renderBackground'; -import renderNormalVoronoi from './renderNormalVoronoi'; -import renderHighlightVoronoi from './renderHighlightVoronoi'; -import renderSelectedVoronoi from './renderSelectedVoronoi'; -import renderTarget from './renderTarget'; -import { VIEW_TYPE, SEARCH_VIEW_TYPE } from 'Types'; - -export default function renderVoronoiView( - ctx, - viewType, - layoutData, - federView, - hoveredCluster = null, -) { - // background - renderBackground(ctx, federView); - - // normal-cluster - renderNormalVoronoi(ctx, viewType, layoutData, federView); - - // nprobe-cluster search - viewType === VIEW_TYPE.search && - renderHighlightVoronoi(ctx, layoutData, federView); - - // hoverCluster - !!hoveredCluster && - renderSelectedVoronoi(ctx, viewType, hoveredCluster, federView); - - // target search - viewType === VIEW_TYPE.search && renderTarget(ctx, SEARCH_VIEW_TYPE.voronoi, layoutData, federView); -} diff --git a/federjs_old/FederView/index.js b/federjs_old/FederView/index.js deleted file mode 100644 index 67941d7..0000000 --- a/federjs_old/FederView/index.js +++ /dev/null @@ -1,97 +0,0 @@ -import { INDEX_TYPE } from 'Types'; -import { initLoadingStyle, renderLoading } from './loading'; -import HnswView from './HnswView'; -import IvfflatView from './IvfflatView'; - -const viewHandlerMap = { - [INDEX_TYPE.hnsw]: HnswView, - [INDEX_TYPE.ivf_flat]: IvfflatView, -}; - -const defaultViewParams = { - width: 1000, - height: 600, - canvasScale: 3, -}; - -export default class FederView { - constructor({ domSelector, viewParams }) { - this.domSelector = domSelector; - this.viewParams = Object.assign({}, defaultViewParams, viewParams); - - this.viewHandler = null; - - // this.initDom(); - initLoadingStyle(); - } - initDom() { - const dom = document.createElement('div'); - dom.id = `feder-dom-${Math.floor(Math.random() * 100000)}`; - const { width, height } = this.viewParams; - const domStyle = { - position: 'relative', - width: `${width}px`, - height: `${height}px`, - // boxShadow: '0 0 5px #ccc', - // borderRadius: '10px', - }; - Object.assign(dom.style, domStyle); - renderLoading(dom, width, height); - - if (this.domSelector) { - const domContainer = document.querySelector(this.domSelector); - domContainer.innerHTML = ''; - domContainer.appendChild(dom); - } - - return dom; - } - initView({ indexType, indexMeta, getVectorById }) { - if (indexType in viewHandlerMap) { - this.view = new viewHandlerMap[indexType]({ - indexMeta, - viewParams: this.viewParams, - getVectorById, - }); - } else throw `No view handler for ${indexType}`; - } - overview(initCoreAndViewPromise) { - const dom = this.initDom(); - initCoreAndViewPromise.then(() => { - this.view.overview(dom); - }); - - return dom; - } - search({ - searchRes = null, - targetMediaUrl = null, - searchResPromise = null, - } = {}) { - const dom = this.initDom(); - - if (searchResPromise) { - searchResPromise.then(({ searchRes, targetMediaUrl }) => { - this.view.search(dom, { - searchRes, - targetMediaUrl, - }); - }); - } else { - this.view.search(dom, { - searchRes, - targetMediaUrl, - }); - } - - return dom; - } - - switchSearchView(searchViewType) { - try { - this.view.switchSearchView(searchViewType); - } catch (e) { - console.log('Not Support', e); - } - } -} diff --git a/federjs_old/FederView/loading.js b/federjs_old/FederView/loading.js deleted file mode 100644 index 3926aa7..0000000 --- a/federjs_old/FederView/loading.js +++ /dev/null @@ -1,89 +0,0 @@ -import * as d3 from 'd3'; - -const loadingSvgId = 'feder-loading'; -const loadingWidth = 30; -const loadingStrokeWidth = 6; - -export const initLoadingStyle = () => { - const style = document.createElement('style'); - style.type = 'text/css'; - style.innerHTML = ` - @keyframes rotation { - from { - transform: translate(${loadingWidth / 2}px,${ - loadingWidth / 2 - }px) rotate(0deg); - } - to { - transform: translate(${loadingWidth / 2}px,${ - loadingWidth / 2 - }px) rotate(359deg); - } - } - .rotate { - animation: rotation 2s infinite linear; - } - `; - document.getElementsByTagName('head').item(0).appendChild(style); -}; - -export const renderLoading = (domNode, width, height) => { - const dom = d3.select(domNode); - // const { width, height } = dom.node().getBoundingClientRect(); - if (!dom.select(`#${loadingSvgId}`).empty()) return; - const svg = dom - .append('svg') - .attr('id', loadingSvgId) - .attr('width', loadingWidth) - .attr('height', loadingWidth) - .style('position', 'absolute') - .style('left', width / 2 - loadingWidth / 2) - .style('bottom', height / 2 - loadingWidth / 2) - .style('overflow', 'visible'); - - const defsG = svg.append('defs'); - const linearGradientId = `feder-loading-gradient`; - const linearGradient = defsG - .append('linearGradient') - .attr('id', linearGradientId) - .attr('x1', 0) - .attr('y1', 0) - .attr('x2', 0) - .attr('y2', 1); - linearGradient - .append('stop') - .attr('offset', '0%') - .style('stop-color', '#1E64FF'); - linearGradient - .append('stop') - .attr('offset', '100%') - .style('stop-color', '#061982'); - - const loadingCircle = svg - .append('circle') - .attr('cx', loadingWidth / 2) - .attr('cy', loadingWidth / 2) - .attr('fill', 'none') - .attr('r', loadingWidth / 2) - .attr('stroke', '#1E64FF') - .attr('stroke-width', loadingStrokeWidth); - - const semiCircle = svg - .append('path') - .attr( - 'd', - `M0,${-loadingWidth / 2} a ${loadingWidth / 2} ${ - loadingWidth / 2 - } 0 1 1 ${0} ${loadingWidth}` - ) - .attr('fill', 'none') - // .style('transform', ``) - .attr('stroke', `url(#${linearGradientId})`) - .attr('stroke-width', loadingStrokeWidth) - .classed('rotate', true); -}; - -export const finishLoading = (domNode) => { - const dom = d3.select(domNode); - dom.selectAll(`#${loadingSvgId}`).remove(); -}; diff --git a/federjs_old/Types.js b/federjs_old/Types.js deleted file mode 100644 index 810251a..0000000 --- a/federjs_old/Types.js +++ /dev/null @@ -1,82 +0,0 @@ -export const SOURCE_TYPE = { - hnswlib: 'hnswlib', - faiss: 'faiss', -}; - -export const INDEX_TYPE = { - hnsw: 'hnsw', - ivf_flat: 'ivf_flat', - flat: 'flat', -}; - -export const PROJECT_METHOD = { - umap: 'umap', - tsne: 'tsne', -} - -// faiss config -export const MetricType = { - METRIC_INNER_PRODUCT: 0, ///< maximum inner product search - METRIC_L2: 1, ///< squared L2 search - METRIC_L1: 2, ///< L1 (aka cityblock) - METRIC_Linf: 3, ///< infinity distance - METRIC_Lp: 4, ///< L_p distance, p is given by a faiss::Index - /// metric_arg - - /// some additional metrics defined in scipy.spatial.distance - METRIC_Canberra: 20, - METRIC_BrayCurtis: 21, - METRIC_JensenShannon: 22, -}; - -export const DirectMapType = { - NoMap: 0, // default - Array: 1, // sequential ids (only for add, no add_with_ids) - Hashtable: 2, // arbitrary ids -}; - -export const IndexHeader = { - IVFFlat: 'IwFl', - FlatL2: 'IxF2', - FlatIR: 'IxFI', -}; - -export const HNSW_NODE_TYPE = { - Coarse: 1, - Candidate: 2, - Fine: 3, - Target: 4, -}; - -export const HNSW_LINK_TYPE = { - None: 0, - Visited: 1, - Extended: 2, - Searched: 3, - Fine: 4, -}; - -export const VIEW_TYPE = { - overview: 'overview', - search: 'search', -} - -export const SEARCH_VIEW_TYPE = { - voronoi: 'voronoi', - polar: 'polar', - project: 'project', -} - -export const ANIMATION_TYPE = { - exit: 'exit', - enter: 'enter', -} - -export const FEDER_CORE_REQUEST = { - get_index_type: "get_index_type", - get_index_meta: "get_index_meta", - get_test_id_and_vector: "get_test_id_and_vector", - get_vector_by_id: "get_vector_by_id", - search: "search", - set_search_params: "set_search_params", -}; diff --git a/federjs_old/Utils/PriorityQueue.js b/federjs_old/Utils/PriorityQueue.js deleted file mode 100644 index b95c76a..0000000 --- a/federjs_old/Utils/PriorityQueue.js +++ /dev/null @@ -1,78 +0,0 @@ -//Minimum Heap -class PriorityQueue { - constructor(arr = [], key = null) { - if (typeof key == 'string') { - this._key = (item) => item[key]; - } else this._key = key; - this._tree = []; - arr.forEach((d) => this.add(d)); - } - add(item) { - this._tree.push(item); - let id = this._tree.length - 1; - while (id) { - const fatherId = Math.floor((id - 1) / 2); - if (this._getValue(id) >= this._getValue(fatherId)) break; - else { - this._swap(fatherId, id); - id = fatherId; - } - } - } - get top() { - return this._tree[0]; - } - pop() { - if (this.isEmpty) { - return 'empty'; - } - const item = this.top; - if (this._tree.length > 1) { - const lastItem = this._tree.pop(); - let id = 0; - this._tree[id] = lastItem; - while (!this._isLeaf(id)) { - const curValue = this._getValue(id); - const leftId = id * 2 + 1; - const leftValue = this._getValue(leftId); - const rightId = leftId >= this._tree.length - 1 ? leftId : id * 2 + 2; - const rightValue = this._getValue(rightId); - const minValue = Math.min(leftValue, rightValue); - if (curValue <= minValue) break; - else { - const minId = leftValue < rightValue ? leftId : rightId; - this._swap(minId, id); - id = minId; - } - } - } else { - this._tree = []; - } - return item; - } - get isEmpty() { - return this._tree.length === 0; - } - get size() { - return this._tree.length; - } - get _firstLeaf() { - return Math.floor(this._tree.length / 2); - } - _isLeaf(id) { - return id >= this._firstLeaf; - } - _getValue(id) { - if (this._key) { - return this._key(this._tree[id]); - } else { - return this._tree[id]; - } - } - _swap(id0, id1) { - const tree = this._tree; - [tree[id0], tree[id1]] = [tree[id1], tree[id0]]; - } -} - -export default PriorityQueue; diff --git a/federjs_old/Utils/index.js b/federjs_old/Utils/index.js deleted file mode 100644 index 6b8718c..0000000 --- a/federjs_old/Utils/index.js +++ /dev/null @@ -1,145 +0,0 @@ -import * as d3 from 'd3'; -export const colorScheme = d3.schemeTableau10; - -import { MetricType } from 'Types'; - -export const getDisL2 = (vec1, vec2) => { - return Math.sqrt( - vec1 - .map((num, i) => num - vec2[i]) - .map((num) => num * num) - .reduce((a, c) => a + c, 0) - ); -}; - -export const getDisIR = (vec1, vec2) => { - return vec1.map((num, i) => num * vec2[i]).reduce((acc, cur) => acc + cur, 0); -}; - -export const getDisFunc = (metricType) => { - if (metricType === MetricType.METRIC_L2) { - return getDisL2; - } else if (metricType === MetricType.METRIC_INNER_PRODUCT) { - return getDisIR; - } - console.warn('[getDisFunc] wrong metric_type, use L2 (default).', metricType); - return getDisL2; -}; - -export default getDisFunc; - -export const getIvfListId = (listId) => `list-${listId}`; -export const uint8toChars = (data) => { - return String.fromCharCode(...data); -}; - -export const generateArray = (num) => { - return Array.from(new Array(Math.floor(num)).keys()); -}; - -export const polyPoints2path = (points, withZ = true) => { - return `M${points.join('L')}${withZ ? 'Z' : ''}`; -}; - -export const calAngle = (x, y) => { - let angle = (Math.atan(x / y) / Math.PI) * 180; - if (angle < 0) { - if (x < 0) { - angle += 360; - } else { - angle += 180; - } - } else { - if (x < 0) { - angle += 180; - } - } - return angle; -}; - -export const vecSort = (vecs, layoutKey, returnKey) => { - const center = { - x: vecs.reduce((acc, c) => acc + c[layoutKey][0], 0) / vecs.length, - y: vecs.reduce((acc, c) => acc + c[layoutKey][1], 0) / vecs.length, - }; - const angles = vecs.map((vec) => ({ - _vecSortAngle: calAngle( - vec[layoutKey][0] - center.x, - vec[layoutKey][1] - center.y - ), - _key: vec[returnKey], - })); - angles.sort((a, b) => a._vecSortAngle - b._vecSortAngle); - const res = angles.map((vec) => vec._key); - return res; -}; - -export const dist2 = (vec1, vec2) => - vec1.map((num, i) => num - vec2[i]).reduce((acc, cur) => acc + cur * cur, 0); - -export const dist = (vec1, vec2) => Math.sqrt(dist2(vec1, vec2)); - -export const inCircle = (x, y, x0, y0, r, bias = 0) => - dist2([x, y], [x0, y0]) < Math.pow(r + bias, 2); - -export const deDupLink = (links, source = 'source', target = 'target') => { - const linkStringSet = new Set(); - return links.filter((link) => { - const linkString = `${link[source]}---${link[target]}`; - const linkStringReverse = `${link[target]}---${link[source]}`; - if (linkStringSet.has(linkString) || linkStringSet.has(linkStringReverse)) { - return false; - } else { - linkStringSet.add(linkString); - return true; - } - }); -}; - -const connection = '---'; -export const getLinkId = (sourceId, targetId) => - `${sourceId}${connection}${targetId}`; -export const parseLinkId = (linkId) => linkId.split(connection).map((d) => +d); -export const getLinkIdWithLevel = (sourceId, targetId, level) => - `link-${level}-${sourceId}-${targetId}`; - -export const getNodeIdWithLevel = (nodeId, level) => `node-${level}-${nodeId}`; -export const getEntryLinkIdWithLevel = (nodeId, level) => - `inter-level-${level}-${nodeId}`; - -export const shortenLine = (point_0, point_1, d = 20) => { - const length = dist(point_0, point_1); - const t = Math.min(d / length, 0.4); - return [ - getInprocessPos(point_0, point_1, t), - getInprocessPos(point_0, point_1, 1 - t), - ]; -}; - -export const getInprocessPos = (point_0, point_1, t) => { - const x = point_0[0] * (1 - t) + point_1[0] * t; - const y = point_0[1] * (1 - t) + point_1[1] * t; - return [x, y]; -}; - -export const showVectors = (vec, precision = 6, maxLength = 20) => { - return ( - vec - .slice(0, maxLength) - .map((num) => num.toFixed(precision)) - .join(', ') + ', ...' - ); -}; - -export const randomSelect = (arr, k) => { - const res = new Set(); - k = Math.min(arr.length, k); - while (k > 0) { - const itemIndex = Math.floor(Math.random() * arr.length); - if (!res.has(itemIndex)) { - res.add(itemIndex); - k -= 1; - } - } - return Array.from(res).map((i) => arr[i]); -}; diff --git a/federjs_old/Utils/renderUtils.js b/federjs_old/Utils/renderUtils.js deleted file mode 100644 index 3e0ccdf..0000000 --- a/federjs_old/Utils/renderUtils.js +++ /dev/null @@ -1,293 +0,0 @@ -import { polyPoints2path } from 'Utils'; -import * as d3 from 'd3'; - -export const colorScheme = d3.schemeTableau10; - -export const ZBlue = '#175FFF'; -export const ZLightBlue = '#91FDFF'; -export const ZYellow = '#FFFC85'; -export const ZOrange = '#F36E4B'; -export const ZLayerBorder = '#D9EAFF'; - -export const whiteColor = '#ffffff'; -export const blackColor = '#000000'; -export const backgroundColor = blackColor; -export const highLightColor = ZYellow; -export const voronoiHighlightColor = ZYellow; -export const selectedColor = '#FFC671'; -export const voronoiHoverColor = selectedColor; -export const hexWithOpacity = (color, opacity) => { - let opacityString = Math.round(opacity * 255).toString(16); - if (opacityString.length < 2) { - opacityString = '0' + opacityString; - } - return color + opacityString; -}; -export const voronoiStrokeWidth = 4; - -export const highLightGradientStopColors = [ - [0, hexWithOpacity(whiteColor, 0.2)], - [1, hexWithOpacity(ZYellow, 1)], -]; - -export const neighbourGradientStopColors = [ - [0, hexWithOpacity(whiteColor, 0)], - [1, hexWithOpacity(whiteColor, 0.8)], -]; - -export const targetLevelGradientStopColors = neighbourGradientStopColors; - -export const normalGradientStopColors = [ - [0, hexWithOpacity('#061982', 0.3)], - [1, hexWithOpacity('#1E64FF', 0.4)], -]; - -export const layerGradientStopColors = [ - [0.1, hexWithOpacity('#1E64FF', 0.4)], - [0.9, hexWithOpacity('#00234D', 0)], -]; - -const draw = ({ - ctx, - drawFunc = () => {}, - fillStyle = '', - strokeStyle = '', - lineWidth = 0, - lineCap = 'butt', - shadowColor = '', - shadowBlur = 0, - shadowOffsetX = 0, - shadowOffsetY = 0, - isFillLinearGradient = false, - isStrokeLinearGradient = false, - gradientPos = [0, 0, 100, 100], - gradientStopColors = [], -}) => { - ctx.save(); - - let gradient = null; - if (isFillLinearGradient || isStrokeLinearGradient) { - gradient = ctx.createLinearGradient(...gradientPos); - gradientStopColors.forEach((stopColor) => - gradient.addColorStop(...stopColor) - ); - } - ctx.fillStyle = isFillLinearGradient ? gradient : fillStyle; - ctx.strokeStyle = isStrokeLinearGradient ? gradient : strokeStyle; - ctx.lineWidth = lineWidth; - ctx.lineCap = lineCap; - - ctx.shadowColor = shadowColor; - ctx.shadowBlur = shadowBlur; - ctx.shadowOffsetX = shadowOffsetX; - ctx.shadowOffsetY = shadowOffsetY; - - drawFunc(); - - ctx.restore(); -}; - -export const drawVoronoi = ({ - ctx, - pointsList, - hasFill = false, - hasStroke = false, - ...styles -}) => { - const drawFunc = () => { - pointsList.forEach((points) => { - const path = new Path2D(polyPoints2path(points)); - hasFill && ctx.fill(path); - hasStroke && ctx.stroke(path); - }); - }; - draw({ ctx, drawFunc, ...styles }); -}; - -export const extraExtent = ([x0, x1], p = 0.5) => { - const length = x1 - x0; - return [x0 - length * p, x1 + length * p]; -}; - -export const drawVoronoiWithDots = ({ - ctx, - points, - hasFill = false, - hasStroke = false, - dotColor = hexWithOpacity('red', 0.6), - dotR = 1.5, - dotAngle = Math.PI / 6, - dotGap = 4, - fillStyle = 'blue', - strokeStyle = 'green', - lineWidth = 2, -}) => { - ctx.save(); - - ctx.fillStyle = fillStyle; - ctx.strokeStyle = strokeStyle; - ctx.lineWidth = lineWidth; - - path = new Path2D(polyPoints2path(points)); - hasFill && ctx.fill(path); - hasStroke && ctx.stroke(path); - - ctx.clip(path); - - const extentX = extraExtent(d3.extent(points, (point) => point[0])); - const extentY = extraExtent(d3.extent(points, (point) => point[1])); - - ctx.fillStyle = dotColor; - const step = (dotR + dotGap) * 2; - d3.range(extentX[0], extentX[1], step).forEach((x) => - d3.range(extentY[0], extentY[1], step).forEach((_y) => { - y = _y + (x - extentX[0]) * Math.tan(dotAngle); - ctx.beginPath(); - ctx.arc(x, y, dotR, 0, 2 * Math.PI); - ctx.fill(); - }) - ); - - ctx.strokeStyle = strokeStyle; - hasStroke && ctx.stroke(path); - - ctx.restore(); -}; - -export const drawCircle = ({ - ctx, - circles, - hasFill = false, - hasStroke = false, - ...styles -}) => { - const drawFunc = () => { - circles.forEach(([x, y, r]) => { - ctx.beginPath(); - ctx.arc(x, y, r, 0, 2 * Math.PI); - hasFill && ctx.fill(); - hasStroke && ctx.stroke(); - }); - }; - draw({ ctx, drawFunc, ...styles }); -}; - -export const drawEllipse = ({ - ctx, - circles, - hasFill = false, - hasStroke = false, - ...styles -}) => { - const drawFunc = () => { - circles.forEach(([x, y, rx, ry]) => { - ctx.beginPath(); - ctx.ellipse(x, y, rx, ry, 0, 0, 2 * Math.PI); - hasFill && ctx.fill(); - hasStroke && ctx.stroke(); - }); - }; - draw({ ctx, drawFunc, ...styles }); -}; - -export const drawRect = ({ - ctx, - x = 0, - y = 0, - width, - height, - hasFill = false, - hasStroke = false, - ...styles -}) => { - const drawFunc = () => { - hasFill && ctx.fillRect(0, 0, width, height); - hasStroke && ctx.strokeRect(0, 0, width, height); - }; - draw({ ctx, drawFunc, ...styles }); -}; - -export const drawPath = ({ - ctx, - points, - hasFill = false, - hasStroke = false, - withZ = true, - ...styles -}) => { - const drawFunc = () => { - const path = new Path2D(polyPoints2path(points, withZ)); - hasFill && ctx.fill(path); - hasStroke && ctx.stroke(path); - }; - draw({ ctx, drawFunc, ...styles }); -}; - -export const drawLine = ({ - ctx, - points, - hasFill = false, - hasStroke = false, - ...styles -}) => { - const drawFunc = () => { - const path = new Path2D(`M${points[0]}L${points[1]}`); - hasFill && ctx.fill(path); - hasStroke && ctx.stroke(path); - }; - draw({ ctx, drawFunc, ...styles }); -}; - -export const drawLines = ({ - ctx, - pointsList, - hasFill = false, - hasStroke = false, - ...styles -}) => { - const drawFunc = () => { - pointsList.forEach((points) => { - const path = new Path2D(`M${points[0]}L${points[1]}`); - hasFill && ctx.fill(path); - hasStroke && ctx.stroke(path); - }); - }; - draw({ ctx, drawFunc, ...styles }); -}; - -export const drawLinesWithLinearGradient = ({ - ctx, - pointsList, - hasFill = false, - hasStroke = false, - isStrokeLinearGradient = true, - ...styles -}) => { - pointsList.forEach((points) => { - const path = new Path2D(`M${points[0]}L${points[1]}`); - const gradientPos = [...points[0], ...points[1]]; - const drawFunc = () => { - hasFill && ctx.fill(path); - hasStroke && ctx.stroke(path); - }; - draw({ ctx, drawFunc, isStrokeLinearGradient, gradientPos, ...styles }); - }); -}; - -export const renderLoading = ({ dom, width, height }) => { - const _dom = d3.select(`#${dom.id}`); - const svg = _dom - .append('svg') - .attr('id', loadingSvgId) - .attr('width', loadingWidth) - .attr('height', loadingWidth) - .style('position', 'absolute') - .style('left', width / 2 - loadingWidth / 2) - .style('bottom', height / 2 - loadingWidth / 2) - .style('border', '1px solid red'); -}; - -export const finishLoading = ({ dom }) => { - const _dom = d3.select(`#${dom.id}`); - _dom.select(`#${loadingSvgId}`).remove(); -}; diff --git a/federjs_old/index.js b/federjs_old/index.js deleted file mode 100644 index 2914b6a..0000000 --- a/federjs_old/index.js +++ /dev/null @@ -1,6 +0,0 @@ -import FederCore from './FederCore'; -import FederView from './FederView'; -import Feder from './Feder'; -import { FEDER_CORE_REQUEST } from 'Types'; - -export { Feder, FederCore, FederView, FEDER_CORE_REQUEST }; diff --git a/package.json b/package.json index 22fd3d3..5cf16b5 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@zilliz/feder", "author": "ued@zilliz.com", - "version": "1.0.3", + "version": "1.0.4", "description": "visualization packages for vector space", "main": "dist/index.js", "files": [ diff --git a/test/bundle.js b/test/bundle.js index 4587fd9..5ee4e0e 100644 --- a/test/bundle.js +++ b/test/bundle.js @@ -12681,6 +12681,185 @@ ${indentData}`); } } + // node_modules/d3-dsv/src/dsv.js + var EOL = {}; + var EOF = {}; + var QUOTE = 34; + var NEWLINE = 10; + var RETURN = 13; + function objectConverter(columns) { + return new Function("d", "return {" + columns.map(function(name, i) { + return JSON.stringify(name) + ": d[" + i + '] || ""'; + }).join(",") + "}"); + } + function customConverter(columns, f) { + var object = objectConverter(columns); + return function(row, i) { + return f(object(row), i, columns); + }; + } + function inferColumns(rows) { + var columnSet = /* @__PURE__ */ Object.create(null), columns = []; + rows.forEach(function(row) { + for (var column in row) { + if (!(column in columnSet)) { + columns.push(columnSet[column] = column); + } + } + }); + return columns; + } + function pad(value, width) { + var s = value + "", length = s.length; + return length < width ? new Array(width - length + 1).join(0) + s : s; + } + function formatYear(year) { + return year < 0 ? "-" + pad(-year, 6) : year > 9999 ? "+" + pad(year, 6) : pad(year, 4); + } + function formatDate(date) { + var hours = date.getUTCHours(), minutes = date.getUTCMinutes(), seconds = date.getUTCSeconds(), milliseconds = date.getUTCMilliseconds(); + return isNaN(date) ? "Invalid Date" : formatYear(date.getUTCFullYear(), 4) + "-" + pad(date.getUTCMonth() + 1, 2) + "-" + pad(date.getUTCDate(), 2) + (milliseconds ? "T" + pad(hours, 2) + ":" + pad(minutes, 2) + ":" + pad(seconds, 2) + "." + pad(milliseconds, 3) + "Z" : seconds ? "T" + pad(hours, 2) + ":" + pad(minutes, 2) + ":" + pad(seconds, 2) + "Z" : minutes || hours ? "T" + pad(hours, 2) + ":" + pad(minutes, 2) + "Z" : ""); + } + function dsv_default(delimiter) { + var reFormat = new RegExp('["' + delimiter + "\n\r]"), DELIMITER = delimiter.charCodeAt(0); + function parse(text, f) { + var convert, columns, rows = parseRows(text, function(row, i) { + if (convert) + return convert(row, i - 1); + columns = row, convert = f ? customConverter(row, f) : objectConverter(row); + }); + rows.columns = columns || []; + return rows; + } + function parseRows(text, f) { + var rows = [], N = text.length, I = 0, n = 0, t, eof = N <= 0, eol = false; + if (text.charCodeAt(N - 1) === NEWLINE) + --N; + if (text.charCodeAt(N - 1) === RETURN) + --N; + function token() { + if (eof) + return EOF; + if (eol) + return eol = false, EOL; + var i, j = I, c2; + if (text.charCodeAt(j) === QUOTE) { + while (I++ < N && text.charCodeAt(I) !== QUOTE || text.charCodeAt(++I) === QUOTE) + ; + if ((i = I) >= N) + eof = true; + else if ((c2 = text.charCodeAt(I++)) === NEWLINE) + eol = true; + else if (c2 === RETURN) { + eol = true; + if (text.charCodeAt(I) === NEWLINE) + ++I; + } + return text.slice(j + 1, i - 1).replace(/""/g, '"'); + } + while (I < N) { + if ((c2 = text.charCodeAt(i = I++)) === NEWLINE) + eol = true; + else if (c2 === RETURN) { + eol = true; + if (text.charCodeAt(I) === NEWLINE) + ++I; + } else if (c2 !== DELIMITER) + continue; + return text.slice(j, i); + } + return eof = true, text.slice(j, N); + } + while ((t = token()) !== EOF) { + var row = []; + while (t !== EOL && t !== EOF) + row.push(t), t = token(); + if (f && (row = f(row, n++)) == null) + continue; + rows.push(row); + } + return rows; + } + function preformatBody(rows, columns) { + return rows.map(function(row) { + return columns.map(function(column) { + return formatValue(row[column]); + }).join(delimiter); + }); + } + function format2(rows, columns) { + if (columns == null) + columns = inferColumns(rows); + return [columns.map(formatValue).join(delimiter)].concat(preformatBody(rows, columns)).join("\n"); + } + function formatBody(rows, columns) { + if (columns == null) + columns = inferColumns(rows); + return preformatBody(rows, columns).join("\n"); + } + function formatRows(rows) { + return rows.map(formatRow).join("\n"); + } + function formatRow(row) { + return row.map(formatValue).join(delimiter); + } + function formatValue(value) { + return value == null ? "" : value instanceof Date ? formatDate(value) : reFormat.test(value += "") ? '"' + value.replace(/"/g, '""') + '"' : value; + } + return { + parse, + parseRows, + format: format2, + formatBody, + formatRows, + formatRow, + formatValue + }; + } + + // node_modules/d3-dsv/src/csv.js + var csv = dsv_default(","); + var csvParse = csv.parse; + var csvParseRows = csv.parseRows; + var csvFormat = csv.format; + var csvFormatBody = csv.formatBody; + var csvFormatRows = csv.formatRows; + var csvFormatRow = csv.formatRow; + var csvFormatValue = csv.formatValue; + + // node_modules/d3-dsv/src/tsv.js + var tsv = dsv_default(" "); + var tsvParse = tsv.parse; + var tsvParseRows = tsv.parseRows; + var tsvFormat = tsv.format; + var tsvFormatBody = tsv.formatBody; + var tsvFormatRows = tsv.formatRows; + var tsvFormatRow = tsv.formatRow; + var tsvFormatValue = tsv.formatValue; + + // node_modules/d3-fetch/src/text.js + function responseText(response) { + if (!response.ok) + throw new Error(response.status + " " + response.statusText); + return response.text(); + } + function text_default3(input, init2) { + return fetch(input, init2).then(responseText); + } + + // node_modules/d3-fetch/src/dsv.js + function dsvParse(parse) { + return function(input, init2, row) { + if (arguments.length === 2 && typeof init2 === "function") + row = init2, init2 = void 0; + return text_default3(input, init2).then(function(response) { + return parse(response, row); + }); + }; + } + var csv2 = dsvParse(csvParse); + var tsv2 = dsvParse(tsvParse); + // node_modules/d3-force/src/center.js function center_default(x3, y3) { var nodes, strength = 1; @@ -14648,7 +14827,12 @@ ${indentData}`); polarOrigin, polarMaxR }) => new Promise((resolve) => { - const { numForceIterations, nonTopkNodeR, canvasScale, polarRadiusUpperBound } = layoutParams; + const { + numForceIterations, + nonTopkNodeR, + canvasScale, + polarRadiusUpperBound + } = layoutParams; const clusterId2cluster = {}; searchViewClusters.forEach((cluster) => clusterId2cluster[cluster.clusterId] = cluster); const searchViewNodes = searchRecords.fineSearchRecords.map(({ id: id2, clusterId, distance }) => ({ @@ -14658,7 +14842,7 @@ ${indentData}`); inTopK: searchRecords.topKVectorIds.indexOf(id2) >= 0 })); searchViewNodes.sort((a2, b) => a2.distance - b.distance); - const minDis = getPercentile(searchViewNodes, "distance", 0); + const minDis = searchViewNodes[Math.min(searchViewNodes.length - 1, 1)].distance; const maxDis = getPercentile(searchViewNodes, "distance", polarRadiusUpperBound); const r = linear2().domain([minDis, maxDis]).range([polarMaxR * 0.2, polarMaxR]).clamp(true); searchViewNodes.forEach((node) => { @@ -14690,6 +14874,7 @@ ${indentData}`); }) => new Promise((resolve) => { const { projectPadding, + staticPanelWidth, width, height, canvasScale, @@ -14708,10 +14893,14 @@ ${indentData}`); searchViewNodes.forEach((node, i) => { node.projection = searchviewNodesProjection[i]; }); - const x3 = linear2().domain(extent(searchViewNodes, (node) => node.projection[0])).range([ + const xRange = targetNode.isLeft_coarseLevel ? [ projectPadding[3] * canvasScale, + (width - projectPadding[1]) * canvasScale - staticPanelWidth * canvasScale + ] : [ + projectPadding[3] * canvasScale + staticPanelWidth * canvasScale, (width - projectPadding[1]) * canvasScale - ]); + ]; + const x3 = linear2().domain(extent(searchViewNodes, (node) => node.projection[0])).range(xRange); const y3 = linear2().domain(extent(searchViewNodes, (node) => node.projection[1])).range([ projectPadding[0] * canvasScale, (height - projectPadding[2]) * canvasScale @@ -14770,7 +14959,8 @@ ${indentData}`); polarRadiusUpperBound: 0.97, nonTopkNodeR: 3, minVoronoiRadius: 5, - projectPadding: [20, 30, 20, 30] + projectPadding: [20, 30, 20, 30], + staticPanelWidth: 240 }; var FederLayoutIvfflat = class { overviewLayoutParams = {}; @@ -15304,7 +15494,11 @@ ${indentData}`); tipLineAngle: Math.PI / 3, tipLineColor: "#FFFC85", tipLineWidth: 2, - mediaContentCount: 9 + mediaContentCount: 9, + staticPanelWidth: 240, + hoveredPanelWidth: 250, + clickedPanelWidth: 210, + getVectorById: async () => [] }; var defaultViewParamsHnsw_default = defaultViewParamsHnsw; @@ -15812,29 +16006,37 @@ ${indentData}`); } // federjs/FederView/hnswView/infoPanelStyles.ts - var staticPanelStyles = ({ width, height, padding }) => ({ + var staticPanelStyles = ({ + height, + staticPanelWidth + }) => ({ position: "absolute", left: "16px", top: "16px", - width: padding ? `${padding[3] + 10}px` : `${width * 0.3}px`, + width: `${staticPanelWidth}px`, "max-height": `${height - 110}px`, overflow: "auto", borderColor: "#FFFFFF", backgroundColor: hexWithOpacity("#000000", 0.6) }); - var clickedPanelStyles = ({ width, height, padding }) => ({ + var clickedPanelStyles = ({ + height, + clickedPanelWidth + }) => ({ position: "absolute", right: "16px", top: "16px", - width: padding ? `${padding[1] - 10}px` : `${width * 0.3}px`, + width: `${clickedPanelWidth}px`, "max-height": `${height - 60}px`, overflow: "auto", borderColor: "#FFFFFF", backgroundColor: hexWithOpacity("#000000", 0.6) }); - var hoveredPanelStyles = ({ width }) => ({ + var hoveredPanelStyles = ({ + hoveredPanelWidth + }) => ({ position: "absolute", - width: `${width * 0.3}px`, + width: `${hoveredPanelWidth}px`, paddingLeft: "6px", left: 0, top: 0 @@ -16621,42 +16823,44 @@ ${indentData}`); transitionReplaceTime: 600, transitionNodesEnterTime: 800, transitionNodesMoveTime: 800, - mediaContentCount: 9 + mediaContentCount: 9, + staticPanelWidth: 240, + hoveredPanelWidth: 250, + clickedPanelWidth: 210, + getVectorById: async () => [] }; var defaultViewParamsIvfflat_default = defaltViewParamsIvfflat; // federjs/FederView/ivfflatView/infoPanelStyles.ts var staticPanelStyles2 = ({ - width, height, - padding + staticPanelWidth }) => ({ position: "absolute", left: "16px", top: "16px", - width: padding ? `${padding[3] + 10}px` : `${width * 0.3}px`, + width: `${staticPanelWidth}px`, "max-height": `${height - 110}px`, overflow: "auto", borderColor: "#FFFFFF", backgroundColor: hexWithOpacity("#000000", 0.6) }); var clickedPanelStyles2 = ({ - width, height, - padding + clickedPanelWidth }) => ({ position: "absolute", right: "16px", top: "16px", - width: padding ? `${padding[1] - 10}px` : `${width * 0.3}px`, + width: `${clickedPanelWidth}px`, "max-height": `${height - 60}px`, overflow: "auto", borderColor: "#FFFFFF", backgroundColor: hexWithOpacity("#000000", 0.6) }); - var hoveredPanelStyles2 = ({ width }) => ({ + var hoveredPanelStyles2 = ({ hoveredPanelWidth }) => ({ position: "absolute", - width: `${width * 0.3}px`, + width: `${hoveredPanelWidth}px`, paddingLeft: "6px", left: 0, top: 0, @@ -16689,6 +16893,7 @@ ${indentData}`); hasBorder: false, content: [ { title: `Row No. ${node.id}` }, + { text: `distance: ${node.distance.toFixed(3)}` }, { text: `belong to cluster-${node.clusterId}` }, @@ -17494,186 +17699,94 @@ ${indentData}`); } }; - // federjs/Utils/loading.ts - var loadingSvgId = "feder-loading"; - var loadingWidth = 30; - var loadingStrokeWidth = 6; - var initLoadingStyle = () => { - const style = document.createElement("style"); - style.type = "text/css"; - style.innerHTML = ` - @keyframes rotation { - from { - transform: translate(${loadingWidth / 2}px,${loadingWidth / 2}px) rotate(0deg); - } - to { - transform: translate(${loadingWidth / 2}px,${loadingWidth / 2}px) rotate(359deg); - } - } - .rotate { - animation: rotation 2s infinite linear; - } - `; - document.getElementsByTagName("head").item(0).appendChild(style); - }; - var renderLoading = (domNode, width, height) => { - const dom = select_default2(domNode); - if (!dom.select(`#${loadingSvgId}`).empty()) - return; - const svg = dom.append("svg").attr("id", loadingSvgId).attr("width", loadingWidth).attr("height", loadingWidth).style("position", "absolute").style("left", width / 2 - loadingWidth / 2).style("bottom", height / 2 - loadingWidth / 2).style("overflow", "visible"); - const defsG = svg.append("defs"); - const linearGradientId = `feder-loading-gradient`; - const linearGradient = defsG.append("linearGradient").attr("id", linearGradientId).attr("x1", 0).attr("y1", 0).attr("x2", 0).attr("y2", 1); - linearGradient.append("stop").attr("offset", "0%").style("stop-color", "#1E64FF"); - linearGradient.append("stop").attr("offset", "100%").style("stop-color", "#061982"); - const loadingCircle = svg.append("circle").attr("cx", loadingWidth / 2).attr("cy", loadingWidth / 2).attr("fill", "none").attr("r", loadingWidth / 2).attr("stroke", "#1E64FF").attr("stroke-width", loadingStrokeWidth); - const semiCircle = svg.append("path").attr("d", `M0,${-loadingWidth / 2} a ${loadingWidth / 2} ${loadingWidth / 2} 0 1 1 ${0} ${loadingWidth}`).attr("fill", "none").attr("stroke", `url(#${linearGradientId})`).attr("stroke-width", loadingStrokeWidth).classed("rotate", true); - }; - var finishLoading = (domNode) => { - const dom = select_default2(domNode); - dom.selectAll(`#${loadingSvgId}`).remove(); - }; - - // federjs/Feder.ts - var Feder = class { - domSelector; - initFederPromise; - federIndex; - viewParams; - federLayout; - indexType; - viewType; - searchParams; - constructor({ - source, - filePath, - domSelector, - viewParams = {} - }) { - this.domSelector = domSelector; - const { viewType = "default" /* default */ } = viewParams; - this.viewType = viewType; - if (!viewParams.mediaContent && !!viewParams.mediaCallback) - viewParams.mediaContent = viewParams.mediaCallback; - this.viewParams = viewParams; - this.searchParams = {}; - this.initFederPromise = new Promise(async (resolve) => { - const arrayBuffer = await fetch(filePath).then((res) => res.arrayBuffer()); - this.federIndex = new FederIndex(source, arrayBuffer); - this.indexType = await this.federIndex.getIndexType(); - this.federLayout = new FederLayout(this.federIndex); - resolve(); - }); - initLoadingStyle(); - } - initDom() { - const { width = 800, height = 480 } = this.viewParams; - const node = create_default("div").style("position", "relative").style("width", width + "px").style("height", height + "px").node(); - renderLoading(node, width, height); - return node; - } - overview() { - const node = this.initDom(); - this.executeAction(node, "overview" /* overview */); - return node; - } - search(target = null, targetMedia = null) { - const node = this.initDom(); - this.executeAction(node, "search" /* search */, { - target, - targetMedia, - searchParams: this.searchParams - }); - return node; - } - executeAction(node, actionType, actionData = null) { - new Promise(async (resolve) => { - await this.initFederPromise; - const visData = await this.federLayout.getVisData({ - actionType, - actionData, - viewType: this.viewType, - layoutParams: this.viewParams - }); - const federView = new FederView(visData, this.viewParams); - node.federView = federView; - finishLoading(node); - node.appendChild(federView.node); - federView.render(); - resolve(); - }); - if (this.domSelector) { - const container = select_default2(this.domSelector); - container.node().appendChild(node); - } - } - searchById(id2) { - const node = this.initDom(); - new Promise(async () => { - await this.initFederPromise; - const target = await this.federIndex.getVectorById(id2); - const targetMedia = this.viewParams.mediaContent(id2); - this.executeAction(node, "search" /* search */, { - target, - targetMedia, - searchParams: this.searchParams - }); - }); - return node; - } - searchByRandTestVec() { - const node = this.initDom(); - new Promise(async () => { - await this.initFederPromise; - const idCount = await this.federIndex.getVectorsCount(); - const id2 = Math.floor(Math.random() * idCount); - const target = await this.federIndex.getVectorById(id2); - const targetMedia = this.viewParams.mediaContent(id2); - this.executeAction(node, "search" /* search */, { - target, - targetMedia, - searchParams: this.searchParams - }); - }); - return node; - } - setSearchParams(params) { - this.searchParams = Object.assign({}, this.searchParams, params); - return this; - } - }; - // test/config.js var local = true; + var hnswSource = "hnswlib"; + var hnswIndexFilePath = local ? "data/hnswlib_hnsw_voc_17k.index" : "https://assets.zilliz.com/hnswlib_hnsw_voc_17k_1f1dfd63a9.index"; var ivfflatSource = "faiss"; var ivfflatIndexFilePath = local ? "data/faiss_ivf_flat_voc_17k.index" : "https://assets.zilliz.com/faiss_ivf_flat_voc_17k_ab112eec72.index"; + var imgNamesFilePath = "https://assets.zilliz.com/voc_names_4cee9440b1.csv"; + var getRowId2name = async () => { + const data = await csv2(imgNamesFilePath); + const rowId2name = (rowId) => data[rowId].name; + return rowId2name; + }; + var name2imgUrl = (name) => `https://assets.zilliz.com/voc2012/JPEGImages/${name}`; + var getRowId2imgUrl = async () => { + const rowId2name = await getRowId2name(); + const rowId2imgUrl = (rowId) => name2imgUrl(rowId2name(rowId)); + return rowId2imgUrl; + }; // test/test_feder_all_in_one.js var testVector = Array(512).fill(0).map((_) => Math.random()); - var test_feder_all_in_one = () => { - const feder = new Feder({ - source: ivfflatSource, - filePath: ivfflatIndexFilePath, - domSelector: "#container", - viewParams: { - width: 1200, - height: 800, - projectParams: { projectSeed: 12315 }, - mediaType: "text", - mediaContent: (id2) => `this is text content of No.${id2}`, - mediaContentCount: 6 - } - }); - feder.overview(); - const view = feder.searchById(112); - setTimeout(() => console.log(view.federView), 1e4); - }; // test/test_feder_separate.js var testVector2 = Array(512).fill(0).map((_) => Math.random()); + var testSearchParams = { + k: 4, + ef: 6, + nprobe: 4 + }; + var test_feder_separate = async () => { + const rowId2imgUrl = await getRowId2imgUrl(); + const faissIvfflatArrayBuffer = await fetch(ivfflatIndexFilePath).then((res) => res.arrayBuffer()); + const ivfflatFederIndex = new FederIndex(ivfflatSource, faissIvfflatArrayBuffer); + const ivfflatFederLayout = new FederLayout(ivfflatFederIndex); + const ivfflatViewParams = { + mediaType: "text", + mediaContent: (id2) => `this is content of No.${id2}`, + mediaContentCount: 5, + getVectorById: (id2) => ivfflatFederIndex.getVectorById(id2) + }; + const ivfflatOverviewVisData = await ivfflatFederLayout.getVisData({ + actionType: "overview" + }); + const ivfflatOverview = new FederView(ivfflatOverviewVisData, ivfflatViewParams); + ivfflatOverview.render(); + document.querySelector("#container").appendChild(ivfflatOverview.node); + const ivfflatSearchVisData = await ivfflatFederLayout.getVisData({ + actionType: "search", + actionData: { + target: testVector2, + searchParams: testSearchParams + }, + viewType: "default", + layoutParams: {} + }); + const ivfflatSearchView = new FederView(ivfflatSearchVisData, ivfflatViewParams); + ivfflatSearchView.render(); + document.querySelector("#container").appendChild(ivfflatSearchView.node); + const hnswlibHnswArrayBuffer = await fetch(hnswIndexFilePath).then((res) => res.arrayBuffer()); + const hnswFederIndex = new FederIndex(hnswSource, hnswlibHnswArrayBuffer); + const hnswFederLayout = new FederLayout(hnswFederIndex); + const hnswViewParams = { + mediaType: "text", + mediaContent: (id2) => `this is content of No.${id2}`, + getVectorById: (id2) => hnswFederIndex.getVectorById(id2) + }; + const hnswOverviewVisData = await hnswFederLayout.getVisData({ + actionType: "overview" + }); + const hnswOverview = new FederView(hnswOverviewVisData, hnswViewParams); + hnswOverview.render(); + document.querySelector("#container").appendChild(hnswOverview.node); + const hnswSearchVisData = await hnswFederLayout.getVisData({ + actionType: "search", + actionData: { + target: testVector2, + searchParams: testSearchParams + }, + viewType: "default", + layoutParams: {} + }); + const hnswSearchView = new FederView(hnswSearchVisData, hnswViewParams); + hnswSearchView.render(); + document.querySelector("#container").appendChild(hnswSearchView.node); + }; // test/index.js window.addEventListener("DOMContentLoaded", async () => { - test_feder_all_in_one(); + test_feder_separate(); }); })(); diff --git a/test/index.js b/test/index.js index fbd680c..55eafd9 100644 --- a/test/index.js +++ b/test/index.js @@ -2,6 +2,6 @@ import { test_feder_all_in_one } from './test_feder_all_in_one'; import { test_feder_separate } from './test_feder_separate'; window.addEventListener('DOMContentLoaded', async () => { - test_feder_all_in_one(); - // test_feder_separate(); + // test_feder_all_in_one(); + test_feder_separate(); }); diff --git a/test/test_feder_all_in_one.js b/test/test_feder_all_in_one.js index af62f2f..127ad2c 100644 --- a/test/test_feder_all_in_one.js +++ b/test/test_feder_all_in_one.js @@ -17,7 +17,8 @@ const testSearchParams = { nprobe: 4, }; -export const test_feder_all_in_one = () => { +export const test_feder_all_in_one = async () => { + const mediaCallback = await getRowId2imgUrl(); const feder = new Feder({ source: ivfflatSource, filePath: ivfflatIndexFilePath, @@ -32,6 +33,8 @@ export const test_feder_all_in_one = () => { }, }); feder.overview(); - const view = feder.searchById(112); + const view = feder + .setSearchParams({ nprobe: 7, k: 8 }) + .searchByRandTestVec(112); setTimeout(() => console.log(view.federView), 10000); }; diff --git a/test/test_feder_separate.js b/test/test_feder_separate.js index c1acc57..ec51910 100644 --- a/test/test_feder_separate.js +++ b/test/test_feder_separate.js @@ -71,10 +71,10 @@ export const test_feder_separate = async () => { const hnswFederLayout = new FederLayout(hnswFederIndex); const hnswViewParams = { - mediaType: 'image', - mediaContent: rowId2imgUrl, - // mediaType: 'text', - // mediaContent: (id) => `this is content of No.${id}`, + // mediaType: 'image', + // mediaContent: rowId2imgUrl, + mediaType: 'text', + mediaContent: (id) => `this is content of No.${id}`, getVectorById: (id) => hnswFederIndex.getVectorById(id), }; const hnswOverviewVisData = await hnswFederLayout.getVisData({