Explorar el Código

change block algorithm

Tatiana Inama hace 6 años
padre
commit
e1c5aa51be
Se han modificado 1 ficheros con 260 adiciones y 221 borrados
  1. 260 221
      src/Heatmap.tsx

+ 260 - 221
src/Heatmap.tsx

@@ -6,20 +6,20 @@ import map from './merseyside-nobg-white.png';
 
 import './Heatmap.css';
 
-const X_SEGMENTS = 50;
+const X_SEGMENTS = 8;
 const Y_SEGMENTS = X_SEGMENTS * 1.25;
 const MAX = 200;
 const output_start = 0,
-      output_end = 240,
-      input_start = 1,
-      input_end = 5;
+output_end = 240,
+input_start = 1,
+input_end = 5;
 const RANGE = (n: number) => output_start + ((output_end - output_start) / (input_end - input_start)) * (n - input_start);
 
 
-type Grid = Array<Array<{
+type Grid = Array<{
   area: paper.Path.Rectangle,
   items: Array<paper.Item>
-}>>
+}>
 
 type HeatmapData = {
   id: [number, number],
@@ -41,7 +41,7 @@ class Heatmap extends Component<HeatmapProps, HeatmapState> {
   
   constructor(props: HeatmapProps) {
     super(props);
-
+    
     this.state = {
       data: [],
       canvas: undefined,
@@ -51,7 +51,7 @@ class Heatmap extends Component<HeatmapProps, HeatmapState> {
       heatmapType: HeatmapType.Amount,
     }
   }
-
+  
   drawShape = (path: string, scale: number, data: HeatmapData) => {
     const _path = new Path();
     _path.importJSON(path);
@@ -61,7 +61,7 @@ class Heatmap extends Component<HeatmapProps, HeatmapState> {
     _path.selected = false;
     return _path;
   }
-
+  
   drawMapData = (data: Result[], canvasWidth: number) => {
     const _data = data.reduce<Array<HeatmapData>>((newData, result) => {
       return [
@@ -81,229 +81,268 @@ class Heatmap extends Component<HeatmapProps, HeatmapState> {
     }, [])
     return _data;
   }
-
-  mkGrid = (canvas: paper.PaperScope): Grid=> {
-    const width = canvas.view.size.width,
-          height = canvas.view.size.height;
-    const gridSectionSize = new Size(width/X_SEGMENTS, height/Y_SEGMENTS);
-    let grid: Grid = [];
-
-    for(let row = 0; row < Y_SEGMENTS; row ++) {
-      grid[row] = [];
-
-      for(let column = 0; column < X_SEGMENTS; column ++) {
-        let rect = new Rectangle({
-          point: [column * gridSectionSize.width, row * gridSectionSize.height],
-          size: gridSectionSize,
-        });
-
-        const area = new Path.Rectangle(rect);
-        const items = canvas.project.activeLayer.getItems({
-          overlapping: rect
-        })
-        
-        grid[row][column] = {
-          area,
-          items
+  intersectItems = (items: paper.Item[], rectItem: paper.Path.Rectangle) => {
+    let matchingItems: paper.Item[] = [];
+    items.forEach(child => {
+      // var childMatrix = child.matrix
+      var bounds = child.bounds;
+      // Regardless of the setting of inside / overlapping, if the
+      // bounds don't even intersect, we can skip this child.
+      if (child.name !== 'crap') {
+        if (rectItem.bounds.intersects(bounds)) {
+          if (rectItem.bounds.contains(bounds) || child.intersects(rectItem)) {
+            matchingItems.push(child);
+          } else {
+            const childContainsCornerOfRect = [rectItem.bounds.topLeft,
+              rectItem.bounds.topRight,
+              rectItem.bounds.bottomLeft,
+              rectItem.bounds.bottomRight].some(x => child.contains(x));
+              if (childContainsCornerOfRect){
+                matchingItems.push(child);
+              }
+            }
+          }
+        }
+      })
+      
+      return matchingItems;
+    }
+    
+    mkChunk = (xCount: number, yCount: number, parent: paper.Rectangle, items: paper.Item[]): {area: paper.Path.Rectangle, items: paper.Item[]}[] => {
+      let ret = [];
+      const width = parent.width / xCount;
+      const height = parent.height / yCount;
+      const size = new Size(width, height);
+      for(let x =0; x < xCount; x++){
+        for(let y =0; y < yCount; y++){
+          let rect = new Rectangle({
+            point: [x*width+parent.left, y*height+parent.top],
+            size,
+          });
+          const area = new Path.Rectangle(rect);
+          area.name = 'crap';
+          ret.push({area, items: this.intersectItems(items, area)});
         }
       }
+      return ret;
     }
-    return grid;
-  }
-
-  mkFillColor = (key: HeatmapType, items: paper.Item[]) => {
-    if (key === HeatmapType.Amount) {
-      return new Color(`rgb(${items.length}, ${items.length}, ${items.length})`);
-    } else {
-      const total = items.reduce<number>((tot, item) => {
-        return item.data[key] ? tot + item.data[key] : tot;
-      }, 0);
-      return total !== 0 ? 
+    mkGrid = (canvas: paper.PaperScope): Grid=> {
+      const all_drawings = canvas.project.activeLayer.children;
+      let grid: Grid = [];
+      const bigChunkSizeX = 5, bigChunkSizeY = 5;
+      const start =performance.now();
+      this.mkChunk(bigChunkSizeX, bigChunkSizeY, canvas.project.activeLayer.bounds, all_drawings).forEach(chunk => {
+        const items = chunk.items;
+        this.mkChunk(X_SEGMENTS, Y_SEGMENTS, chunk.area.bounds, items).forEach(smallchunk => {
+          grid.push({
+            area: smallchunk.area,
+            items: smallchunk.items
+          })
+        });
+      }
+      )
+      
+      // const items = canvas.project.activeLayer.getItems({
+      //    overlapping: rect
+      // })
+      console.log(performance.now() - start);
+      
+      //console.log('orig is slower by: ', total1-total2, 'total was', total1);
+      return grid;
+    }
+    
+    
+    mkFillColor = (key: HeatmapType, items: paper.Item[]) => {
+      if (key === HeatmapType.Amount) {
+        return new Color(`rgb(${items.length}, ${items.length}, ${items.length})`);
+      } else {
+        const total = items.reduce<number>((tot, item) => {
+          return item.data[key] ? tot + item.data[key] : tot;
+        }, 0);
+        return total !== 0 ? 
         new Color({ hue: RANGE(total / items.length), saturation: 1, brightness: items.length/MAX}) : 
         new Color('black')
+      }
     }
-  }
-
-  changeHeatmapColor = (type: HeatmapType, grid: Grid) => {
-    grid.forEach(section => {
-      section.forEach(({ area, items }) => {
+    
+    changeHeatmapColor = (type: HeatmapType, grid: Grid) => {
+      grid.forEach(({ area, items }) => {
         area.fillColor = this.mkFillColor(type, items);
       })
-    })
-    this.setState({
-      heatmapType: type
-    })
-  }
-
-  showMap = (canvas: paper.PaperScope) => {
-    if (this.state.mapLayer) {
-      this.state.mapLayer.bringToFront();
-    } else {
-      const mapLayer = new Layer({ name: 'mapLayer' });
-      const raster = new Raster(map);
-      raster.onLoad = () => {
-        raster.position = canvas.view.center;
-        raster.size = canvas.view.viewSize;
+      this.setState({
+        heatmapType: type
+      })
+    }
+    
+    showMap = (canvas: paper.PaperScope) => {
+      if (this.state.mapLayer) {
+        this.state.mapLayer.bringToFront();
+      } else {
+        const mapLayer = new Layer({ name: 'mapLayer' });
+        const raster = new Raster(map);
+        raster.onLoad = () => {
+          raster.position = canvas.view.center;
+          raster.size = canvas.view.viewSize;
+        }
+        mapLayer.addChild(raster);
+        mapLayer.bringToFront();
+        this.setState({ mapLayer });
       }
-      mapLayer.addChild(raster);
-      mapLayer.bringToFront();
-      this.setState({ mapLayer });
     }
-  }
-
-  mkAmountCounter = () => {
-    const counter = new PointText({
-      point: [0, 0],
-      content: '',
-      fillColor: 'white'
-    });
-  
-    this.setState({
-      areaAmount: counter
-    })
-  }
-
-  saveAsImage = () => {
-    const canvas = document.getElementById("vom-heatmap-canvas");
-    //@ts-ignore
-    canvas.toBlob((blob) => {
-      FileSaver.saveAs(blob, `vom-${this.state.heatmapType}.png`);
-    })
-  }
-
-  componentDidMount = () => {
-    const canvas = new PaperScope();
-    canvas.setup('vom-heatmap-canvas');
-    canvas.view.viewSize.height = canvas.view.size.width * 1.25;
-    const data = this.drawMapData(this.props.data, canvas.view.size.width);
-    const grid = this.mkGrid(canvas);
-    this.mkAmountCounter();
-    this.changeHeatmapColor(HeatmapType.Amount, grid);
-    this.showMap(canvas);
-    this.setState({
-      data,
-      canvas,
-      grid,
-    })
-  }
-
-  render = () => (
-    <Container className="vom-heatmap">
+    
+    mkAmountCounter = () => {
+      const counter = new PointText({
+        point: [0, 0],
+        content: '',
+        fillColor: 'white'
+      });
+      
+      this.setState({
+        areaAmount: counter
+      })
+    }
+    
+    saveAsImage = () => {
+      const canvas = document.getElementById("vom-heatmap-canvas");
+      //@ts-ignore
+      canvas.toBlob((blob) => {
+        FileSaver.saveAs(blob, `vom-${this.state.heatmapType}.png`);
+      })
+    }
+    
+    componentDidMount = () => {
+      const canvas = new PaperScope();
+      canvas.setup('vom-heatmap-canvas');
+      canvas.view.viewSize.height = canvas.view.size.width * 1.25;
+      const data = this.drawMapData(this.props.data, canvas.view.size.width);
+      const grid = this.mkGrid(canvas);
+      this.mkAmountCounter();
+      this.changeHeatmapColor(HeatmapType.Amount, grid);
+      this.showMap(canvas);
+      this.setState({
+        data,
+        canvas,
+        grid,
+      })
+    }
+    
+    render = () => (
+      <Container className="vom-heatmap">
       <Row>
-        <Col xs="12" md="8">
-          <div className="vom-heatmap-map">
-            {
-              this.state.heatmapType !== HeatmapType.Amount ? (
-                <div id="vom-heatmap-range">
-                  <div>1</div>
-                  <div>2</div>
-                  <div>3</div>
-                  <div>4</div>
-                  <div>5</div>
-                </div>
-              ) : (
-                <div id="vom-heatmap-amount-range">
-                  <div>1</div>
-                  <div>200</div>
-                </div>
-              )
-            }
-            <canvas id="vom-heatmap-canvas"></canvas>
+      <Col xs="12" md="8">
+      <div className="vom-heatmap-map">
+      {
+        this.state.heatmapType !== HeatmapType.Amount ? (
+          <div id="vom-heatmap-range">
+          <div>1</div>
+          <div>2</div>
+          <div>3</div>
+          <div>4</div>
+          <div>5</div>
+          </div>
+          ) : (
+            <div id="vom-heatmap-amount-range">
+            <div>1</div>
+            <div>200</div>
+            </div>
+            )
+          }
+          <canvas id="vom-heatmap-canvas"></canvas>
           </div>
-        </Col>
-        <Col>
+          </Col>
+          <Col>
           <div className="vom-heatmap-actions">
-            <FormGroup>
-              <Button size="sm" onClick={() => {this.saveAsImage()} }>download current map</Button>
-              <h4>Heatmap type</h4> 
-              <div>
-                <CustomInput type="radio" id="heatmap-by-amount" name="by-amount" 
-                  label="by amount of responses"
-                  value={HeatmapType.Amount}
-                  checked={this.state.heatmapType === HeatmapType.Amount}
-                  onChange={ e => { this.changeHeatmapColor(e.currentTarget.value as HeatmapType, this.state.grid)}}
-                />
-                <CustomInput type="radio" id="heatmap-by-friendliness" name="by-friendliness"
-                  label="by friendliness"
-                  value={HeatmapType.Friendliness}
-                  checked={this.state.heatmapType === HeatmapType.Friendliness}
-                  onChange={ e => { this.changeHeatmapColor(e.currentTarget.value as HeatmapType, this.state.grid)}}
-                />
-                <CustomInput type="radio" id="heatmap-by-correctness" name="by-correctness"
-                  label="by correctness"
-                  value={HeatmapType.Correctness}
-                  checked={this.state.heatmapType === HeatmapType.Correctness}
-                  onChange={ e => { this.changeHeatmapColor(e.currentTarget.value as HeatmapType, this.state.grid)}}
-                />
-                <CustomInput type="radio" id="heatmap-by-pleasantness" name="by-pleasantness"
-                  label="by pleasantness"
-                  value={HeatmapType.Pleasantness}
-                  checked={this.state.heatmapType === HeatmapType.Pleasantness}
-                  onChange={ e => { this.changeHeatmapColor(e.currentTarget.value as HeatmapType, this.state.grid)}}
-                />
-                <CustomInput type="radio" id="heatmap-by-trustworthiness" name="by-trustworthiness"
-                  label="by trustworthiness"
-                  value={HeatmapType.Trustworthiness}
-                  checked={this.state.heatmapType === HeatmapType.Trustworthiness}
-                  onChange={ e => { this.changeHeatmapColor(e.currentTarget.value as HeatmapType, this.state.grid)}}
-                />
-              </div>
-            </FormGroup>
+          <FormGroup>
+          <Button size="sm" onClick={() => {this.saveAsImage()} }>download current map</Button>
+          <h4>Heatmap type</h4> 
+          <div>
+          <CustomInput type="radio" id="heatmap-by-amount" name="by-amount" 
+          label="by amount of responses"
+          value={HeatmapType.Amount}
+          checked={this.state.heatmapType === HeatmapType.Amount}
+          onChange={ e => { this.changeHeatmapColor(e.currentTarget.value as HeatmapType, this.state.grid)}}
+          />
+          <CustomInput type="radio" id="heatmap-by-friendliness" name="by-friendliness"
+          label="by friendliness"
+          value={HeatmapType.Friendliness}
+          checked={this.state.heatmapType === HeatmapType.Friendliness}
+          onChange={ e => { this.changeHeatmapColor(e.currentTarget.value as HeatmapType, this.state.grid)}}
+          />
+          <CustomInput type="radio" id="heatmap-by-correctness" name="by-correctness"
+          label="by correctness"
+          value={HeatmapType.Correctness}
+          checked={this.state.heatmapType === HeatmapType.Correctness}
+          onChange={ e => { this.changeHeatmapColor(e.currentTarget.value as HeatmapType, this.state.grid)}}
+          />
+          <CustomInput type="radio" id="heatmap-by-pleasantness" name="by-pleasantness"
+          label="by pleasantness"
+          value={HeatmapType.Pleasantness}
+          checked={this.state.heatmapType === HeatmapType.Pleasantness}
+          onChange={ e => { this.changeHeatmapColor(e.currentTarget.value as HeatmapType, this.state.grid)}}
+          />
+          <CustomInput type="radio" id="heatmap-by-trustworthiness" name="by-trustworthiness"
+          label="by trustworthiness"
+          value={HeatmapType.Trustworthiness}
+          checked={this.state.heatmapType === HeatmapType.Trustworthiness}
+          onChange={ e => { this.changeHeatmapColor(e.currentTarget.value as HeatmapType, this.state.grid)}}
+          />
           </div>
-        </Col>
-      </Row>
-    </Container>
-  )
-}
-
-type PersonalInformation = {
-  age: string,
-  gender: string,
-  genderCustom: string,
-  birthPlace: string,
-  currentPlace: string,
-  levelEducation: string[],
-  nonNative: string,
-}
-
-type FormPath = {
-  name: string,
-  soundExample: string,
-  associations: string[],
-  correctness: number,
-  friendliness: number,
-  pleasantness: number,
-  trustworthiness: number
-}
-
-type OriginalCanvasData = {
-  form: FormPath,
-  path: string,
-}
-
-type Result = {
-  personalInformation: PersonalInformation,
-  canvas: OriginalCanvasData[],
-  canvasSize: {
-    width: number,
-    height: number
-  },
-  email: string,
-  id: number,
-}
-
-type HeatmapProps = {
-  data: Result[],
-}
-
-type HeatmapState = {
-  data: HeatmapData[],
-  canvas?: paper.PaperScope,
-  grid: Grid,
-  mapLayer: paper.Layer | undefined,
-  areaAmount: paper.TextItem | undefined,
-  heatmapType: HeatmapType,
-}
-
-
-export default Heatmap;
+          </FormGroup>
+          </div>
+          </Col>
+          </Row>
+          </Container>
+          )
+        }
+        
+        type PersonalInformation = {
+          age: string,
+          gender: string,
+          genderCustom: string,
+          birthPlace: string,
+          currentPlace: string,
+          levelEducation: string[],
+          nonNative: string,
+        }
+        
+        type FormPath = {
+          name: string,
+          soundExample: string,
+          associations: string[],
+          correctness: number,
+          friendliness: number,
+          pleasantness: number,
+          trustworthiness: number
+        }
+        
+        type OriginalCanvasData = {
+          form: FormPath,
+          path: string,
+        }
+        
+        type Result = {
+          personalInformation: PersonalInformation,
+          canvas: OriginalCanvasData[],
+          canvasSize: {
+            width: number,
+            height: number
+          },
+          email: string,
+          id: number,
+        }
+        
+        type HeatmapProps = {
+          data: Result[],
+        }
+        
+        type HeatmapState = {
+          data: HeatmapData[],
+          canvas?: paper.PaperScope,
+          grid: Grid,
+          mapLayer: paper.Layer | undefined,
+          areaAmount: paper.TextItem | undefined,
+          heatmapType: HeatmapType,
+        }
+        
+        
+        export default Heatmap;