Added caching for render sheet viewport for O(n)

This commit is contained in:
Eugene 2023-07-24 16:35:38 +03:00
parent 3f896f5603
commit 81bf284591
4 changed files with 124 additions and 44 deletions

View File

@ -22,7 +22,7 @@ export class Sheet {
this.element = canvas this.element = canvas
const ctx = this.element.getContext('2d') const ctx = this.element.getContext('2d')
if(!ctx) throw new Error('Enable hardware acceleration') if (!ctx) throw new Error('Enable hardware acceleration')
this.ctx = ctx this.ctx = ctx
} }
@ -30,17 +30,17 @@ export class Sheet {
getCellByCoords(x: number, y: number): Position { getCellByCoords(x: number, y: number): Position {
let row = 0; let row = 0;
let height = 0 let height = 0
while(height <= y) { while (height <= y) {
height += this.root.config.rows[row].height height += this.root.config.rows[row].height
if(height >= y) break; if (height >= y) break;
row++; row++;
} }
let col = 0; let col = 0;
let width = 0; let width = 0;
while(width <= x) { while (width <= x) {
width += this.root.config.columns[col].width width += this.root.config.columns[col].width
if(width >= x) break; if (width >= x) break;
col++; col++;
} }
@ -48,7 +48,7 @@ export class Sheet {
} }
renderCell(position: Position) { renderCell(position: Position) {
const {column, row} = position const { column, row } = position
this.root.data[row][column].render(this.root) this.root.data[row][column].render(this.root)
} }
@ -58,11 +58,11 @@ export class Sheet {
const lastRowIdx = this.root.viewport.lastRow + 3 const lastRowIdx = this.root.viewport.lastRow + 3
const firstColIdx = this.root.viewport.firstCol const firstColIdx = this.root.viewport.firstCol
for(let row = firstRowIdx; row <= lastRowIdx; row++) { for (let row = firstRowIdx; row <= lastRowIdx; row++) {
for(let col = firstColIdx; col <= lastColIdx; col++ ) { for (let col = firstColIdx; col <= lastColIdx; col++) {
if(!this.root.config.columns[col] || !this.root.config.rows[row]) break; //* Prevent read undefined if (!this.root.config.columns[col] || !this.root.config.rows[row]) break; //* Prevent read undefined
this.renderCell({column: col, row}) this.renderCell({ column: col, row })
} }
} }

View File

@ -11,6 +11,7 @@ import { Styles } from "./modules/styles";
import { Viewport } from "./modules/viewport"; import { Viewport } from "./modules/viewport";
import './scss/main.scss' import './scss/main.scss'
import { createSampleConfig, createSampleData } from "./utils/createData"; import { createSampleConfig, createSampleData } from "./utils/createData";
import { Cache, CachedColumn, CachedRow } from "./modules/cache";
/* /*
! Component structure ! Component structure
@ -41,21 +42,23 @@ export class Spreadsheet {
public data: Cell[][] public data: Cell[][]
public viewport: Viewport public viewport: Viewport
public selection: Selection public selection: Selection
public cache: Cache
constructor(target: string | HTMLElement, props?: SpreadsheetConstructorProperties) { constructor(target: string | HTMLElement, props?: SpreadsheetConstructorProperties) {
const config = createSampleConfig(750, 750) const config = createSampleConfig(10000, 600)
if (props?.view) { if (props?.view) {
config.view = props.view config.view = props.view
} }
this.config = new Config(config) this.config = new Config(config)
this.sheet = new Sheet(this) this.sheet = new Sheet(this)
const data = createSampleData(750, 750) const data = createSampleData(10000, 600)
this.table = new Table(this) this.table = new Table(this)
this.scroller = new Scroller(this) this.scroller = new Scroller(this)
this.toolbar = new Toolbar(this) this.toolbar = new Toolbar(this)
this.header = new Header(this) this.header = new Header(this)
this.editor = new Editor(this) this.editor = new Editor(this)
this.cache = this.getInitialCache()
this.viewport = new Viewport(this, this.scroller.getViewportBoundlingRect()) this.viewport = new Viewport(this, this.scroller.getViewportBoundlingRect())
this.selection = new Selection() this.selection = new Selection()
@ -66,6 +69,42 @@ export class Spreadsheet {
this.appendTableToTarget(target) this.appendTableToTarget(target)
} }
private getInitialCache(): Cache {
const cachedCols: CachedColumn[] = []
let currentWidth = 0
for(let i = 0; i <= this.config.columns.length - 1; i++) {
const col = this.config.columns[i]
currentWidth += col.width
const cacheCol = new CachedColumn({
xPos: currentWidth,
colIdx: i
})
cachedCols.push(cacheCol)
}
const cachedRows: CachedRow[] = []
let currentHeight = 0
for(let i = 0; i <= this.config.rows.length - 1; i++) {
const row = this.config.rows[i]
currentHeight += row.height
const cacheRow = new CachedRow({
yPos: currentHeight,
rowIdx: i
})
cachedRows.push(cacheRow)
}
const cache = new Cache({
columns: cachedCols,
rows: cachedRows
})
console.log("CACHE: ", cache)
console.log("CONFIG: ", this.config)
return cache
}
private buildComponent(): void { private buildComponent(): void {
const content = document.createElement('div') //* Abstract const content = document.createElement('div') //* Abstract

67
src/modules/cache.ts Normal file
View File

@ -0,0 +1,67 @@
export interface CachedColumnProperties {
xPos: number
colIdx: number
}
export class CachedColumn {
xPos: number
colIdx: number
constructor(props: CachedColumnProperties) {
this.xPos = props.xPos
this.colIdx = props.colIdx
}
}
export interface CachedRowProperties {
yPos: number
rowIdx: number
}
export class CachedRow {
yPos: number
rowIdx: number
constructor(props: CachedRowProperties) {
this.yPos = props.yPos
this.rowIdx = props.rowIdx
}
}
export interface CacheConstructorProps {
columns: CachedColumn[]
rows: CachedRow[]
}
export class Cache {
public columns: CachedColumn[]
public rows: CachedRow[]
constructor(initial: CacheConstructorProps) {
this.columns = initial.columns
this.rows = initial.rows
}
public getRowByYCoord(y: number): number {
let rowIdx = 0;
for (let i = 0; i < this.rows.length; i++) {
if (y <= this.rows[i].yPos) { //* Intersection detect
rowIdx = i;
break;
}
}
return rowIdx;
}
public getColumnByXCoord(x: number): number {
let colIdx = 0;
for (let i = 0; i < this.columns.length; i++) {
if (x <= this.columns[i].xPos) { //* Intersection detect
colIdx = i;
break;
}
}
return colIdx;
}
}

View File

@ -55,49 +55,23 @@ export class Viewport {
/** Get index of first row in viewport */ /** Get index of first row in viewport */
private getFirstRow(): number { private getFirstRow(): number {
let rowIdx = 0 let rowIdx = this.root.cache.getRowByYCoord(this.top)
for (let idx = 0, currHeight = 0; currHeight <= this.top; idx++) {
currHeight += this.root.config.rows[idx].height
rowIdx = idx
}
return rowIdx return rowIdx
} }
private getLastRow(): number { private getLastRow(): number {
let rowIdx = this.getFirstRow() let rowIdx = this.root.cache.getRowByYCoord(this.bottom)
let height = this.top
while (height <= this.bottom) {
height += this.root.config.rows[rowIdx].height
if (height >= this.bottom) break;
rowIdx++;
}
return rowIdx return rowIdx
} }
private getFirstCol(): number { private getFirstCol(): number {
let colIdx = 0; let colIdx = this.root.cache.getColumnByXCoord(this.left)
let currWidth = 0
while (currWidth <= this.left) {
currWidth += this.root.config.columns[colIdx].width
if (currWidth >= this.left) break;
colIdx += 1
}
return colIdx return colIdx
} }
private getLastCol(): number { private getLastCol(): number {
let colIdx = this.getFirstCol() let colIdx = this.root.cache.getColumnByXCoord(this.right)
let width = this.left
while (width <= this.right) {
width += this.root.config.columns[colIdx].width
if (width >= this.right) break;
colIdx++;
}
return colIdx return colIdx
} }