Tatiana Inama 6 лет назад
Родитель
Сommit
3a649d643d
1 измененных файлов с 191 добавлено и 156 удалено
  1. 191 156
      src/Heatmap.tsx

+ 191 - 156
src/Heatmap.tsx

@@ -1,18 +1,18 @@
 import React, { Component } from 'react';
-import { Path, PaperScope, Point, Size, Rectangle, Color, Layer, Raster, PointText } from 'paper';
+import { Path, PaperScope, Point, Size, Rectangle, Color, Layer, Raster, PointText, Group } from 'paper';
 import { Container, CustomInput, FormGroup, Row, Col, Button } from 'reactstrap';
 import FileSaver from 'file-saver';
 import map from './merseyside-nobg-white.png';
 
 import './Heatmap.css';
 
-const X_SEGMENTS = 36;
+const X_SEGMENTS = 12;
 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);
 
 
@@ -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);
@@ -59,9 +59,10 @@ class Heatmap extends Component<HeatmapProps, HeatmapState> {
     _path.data = data;
     _path.strokeWidth = 0.5;
     _path.selected = false;
+    _path.visible = false;
     return _path;
   }
-
+  
   drawMapData = (data: Result[], canvasWidth: number) => {
     const _data = data.reduce<Array<HeatmapData>>((newData, result) => {
       return [
@@ -81,31 +82,56 @@ 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 ++) {
-      for(let column = 0; column < X_SEGMENTS; column ++) {
-        let rect = new Rectangle({
-          point: [column * gridSectionSize.width, row * gridSectionSize.height],
-          size: gridSectionSize,
+  
+  mkChunk = (xCount: number, yCount: number, parent: paper.Rectangle, group: paper.Group): {rectangle: paper.Rectangle, items: paper.Item[]}[] => {
+    let chunks = [];
+    const width = parent.width / xCount;
+    const height = parent.height / yCount;
+    const size = new Size(width, height);
+    for(let y = 0; y < yCount; y++){
+      for(let x = 0; x < xCount; x++){
+        let rectangle = new Rectangle({
+          point: [(x*width)+parent.left, (y*height)+parent.top],
+          size,
         });
-
-        const area = new Path.Rectangle(rect);
-        const items = canvas.project.activeLayer.getItems({
-          overlapping: rect
-        })
-        
-        grid.push({area, items});
+        const items = group.getItems({overlapping: rectangle})
+        chunks.push({rectangle, items});
       }
     }
+    
+    return chunks;
+  }
+  
+  mkGrid = (canvas: paper.PaperScope): Grid=> {
+    let grid: Grid = [];
+    const bigChunkSizeX = 4, bigChunkSizeY = 5;
+    const mainLayer = canvas.project.activeLayer;
+    this.mkChunk(bigChunkSizeX*X_SEGMENTS, Y_SEGMENTS*bigChunkSizeY, canvas.project.view.bounds, mainLayer).forEach(({rectangle, items}) => {
+      grid.push({
+        area: new Path.Rectangle(rectangle),
+        items
+      })
+    })
+    // this.mkChunk(bigChunkSizeX, bigChunkSizeY, canvas.project.view.bounds, mainLayer, true).forEach((chunk, i) => {
+    //   const x = new Path.Rectangle(chunk.rectangle);
+    //   console.log(x.bounds)
+    //   x.strokeColor = new Color('red');
+    //   x.strokeWidth = 3;
+    //   const items = chunk.items;
+    //   const group = new Group(items);
+    //   this.mkChunk(X_SEGMENTS, Y_SEGMENTS, chunk.rectangle, group).forEach(smallchunk => {
+    //     const area = new Path.Rectangle(smallchunk.rectangle);
+    //     grid.push({
+    //       area,
+    //       items: smallchunk.items
+    //     })
+    //   });
+    // });
+    
     return grid;
   }
-
+  
+  
   mkFillColor = (key: HeatmapType, items: paper.Item[]) => {
     if (key === HeatmapType.Amount) {
       return new Color(`rgb(${items.length}, ${items.length}, ${items.length})`);
@@ -114,23 +140,23 @@ class Heatmap extends Component<HeatmapProps, HeatmapState> {
         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')
+      new Color({ hue: RANGE(total / items.length), saturation: 1, brightness: items.length/MAX}) : 
+      new Color('black')
     }
   }
-
+  
   changeHeatmapColor = (type: HeatmapType, grid: Grid) => {
-    grid.forEach(({area, items}) => {
-      area.fillColor = this.mkFillColor(type, items);
-    })
-    this.setState({
-      heatmapType: type
+    grid.forEach(({ area, items }) => {
+      const color = this.mkFillColor(type, items);
+      area.fillColor = color;
+      area.strokeColor = color;
     })
   }
-
+  
   showMap = (canvas: paper.PaperScope) => {
     if (this.state.mapLayer) {
       this.state.mapLayer.bringToFront();
+      return this.state.mapLayer;
     } else {
       const mapLayer = new Layer({ name: 'mapLayer' });
       const raster = new Raster(map);
@@ -140,10 +166,9 @@ class Heatmap extends Component<HeatmapProps, HeatmapState> {
       }
       mapLayer.addChild(raster);
       mapLayer.bringToFront();
-      this.setState({ mapLayer });
+      return mapLayer;
     }
   }
-
   saveAsImage = () => {
     const canvas = document.getElementById("vom-heatmap-canvas");
     //@ts-ignore
@@ -151,139 +176,149 @@ class Heatmap extends Component<HeatmapProps, HeatmapState> {
       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.changeHeatmapColor(HeatmapType.Amount, grid);
-    this.showMap(canvas);
+    this.changeHeatmapColor(HeatmapType.Friendliness, grid);
+    
     this.setState({
       data,
       canvas,
       grid,
+      heatmapType: HeatmapType.Friendliness,
+      mapLayer: this.showMap(canvas)
     })
   }
 
+  handleChangeColor = (e: React.ChangeEvent<HTMLInputElement>) => {
+    const heatmapType = e.currentTarget.value as HeatmapType;
+    this.changeHeatmapColor(heatmapType, this.state.grid);
+    this.setState({
+      heatmapType
+    });
+  }
+  
   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>
+    <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>
+        </div>
         </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>
-          </div>
+        <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={ this.handleChangeColor }
+        />
+        <CustomInput type="radio" id="heatmap-by-friendliness" name="by-friendliness"
+        label="by friendliness"
+        value={HeatmapType.Friendliness}
+        checked={this.state.heatmapType === HeatmapType.Friendliness}
+        onChange={ this.handleChangeColor }
+        />
+        <CustomInput type="radio" id="heatmap-by-correctness" name="by-correctness"
+        label="by correctness"
+        value={HeatmapType.Correctness}
+        checked={this.state.heatmapType === HeatmapType.Correctness}
+        onChange={ this.handleChangeColor }
+        />
+        <CustomInput type="radio" id="heatmap-by-pleasantness" name="by-pleasantness"
+        label="by pleasantness"
+        value={HeatmapType.Pleasantness}
+        checked={this.state.heatmapType === HeatmapType.Pleasantness}
+        onChange={ this.handleChangeColor }
+        />
+        <CustomInput type="radio" id="heatmap-by-trustworthiness" name="by-trustworthiness"
+        label="by trustworthiness"
+        value={HeatmapType.Trustworthiness}
+        checked={this.state.heatmapType === HeatmapType.Trustworthiness}
+        onChange={ this.handleChangeColor }
+        />
+        </div>
+        </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;
+        </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;