Преобразование сложных форм svg в абстракцию круга

SVG уродливые, пожалуйста, просмотрите мои:

JSFIDDLE LINK

HTML:

<svg version="1.1" class="overlap-svg" id="alaska"></svg>
<svg version="1.1" class="overlap-svg" id="grid"></svg>

CSS

.overlap-svg {
    position: absolute;
    left:0;
    top: 0;
}

Вопрос:

Если мы перекрываем эти 2 svgs, что бы функция JS была выделить только круги SVG, которые имеют в них части аляски (красного) в них?

описание обзора ниже для получения дополнительной информации


  • Предположим, у вас сложная форма, такая как план аляски.

введите описание изображения здесь

  1. Допустим, у вас есть еще один svg сетки кругов:

введите описание изображения здесь


Как преобразовать это:

введите описание изображения здесь

В нечто подобное:

введите описание изображения здесь

круг должен быть заполнен красным, если какая-либо часть аляски (красная) находится внутри области круга.

Снова просмотрите мою ссылку JSFiddle выше.

Ответ 1

fiddle

Пример вывода с измененным размером элемента холста. Вы можете взять svg и загрузить его в элемент холста. Возьмите элемент, и поскольку это элемент canvas, вы можете получить массив своих пикселей.

Ваша абстракция окружения может быть построена путем построения сетки из пикселей с соответствующим образом измененным размером холста.

Сначала помощник: Grid Manager.

function GridManager(configIn) {
  var gm_ = {};

  gm_.config = {
    'gridWidth': 10,
    'gridHeight': 10,
    'gridCellWidth': 10,
    'gridCellHeight': 10,
    'gridHeight': 100,
    'dataSrc': []
  };

  // Load new config over defaults
  for (var property in configIn) {
    gm_.config[property] = configIn[property];
  }

  /** 
    * Creates an array using the module config building a 2d data array 
    * from a flat array. Loops over GridManager.config.dataSrc
    * 
    * Render a checkerboard pattern:
    *   GridManager.config.dataSrc = ["#"," "]
    * 
    * Render you can load a image by passing in its full pixel array, 
    * provided image height and width match GridManager.config.gridHeight
    * and GridManager.config.gridWidth. 
    */
  gm_.createGridSrc = function() {
    var height = this.config.gridHeight;
    var width = this.config.gridWidth;
    var output = [];

    for (var i = 0; i < height; i++) {
      output[i] = [];

      for (var ii = 0; ii < width; ii++) {
        if (this.config.dataSrc !== undefined) {
          var dataSrc = this.config.dataSrc;
          output[i][ii] = dataSrc[i*width + ii % dataSrc.length];
        }
      }
    }
    return output;
  };

  /** 
    * Creates a SVG with a grid of circles based on
    * GridManager.config.dataSrc.
    * 
    * This is where you can customize GridManager output.
    */
  gm_.createSvgGrid = function() {
    var cellWidth = this.config.gridCellWidth;
    var cellHeight = this.config.gridCellHeight;
    var svgWidth = 1000;
    var svgHeight = 1000;
    var radius = 3
    var cellOffset = radius / 2;

    //create svg
    var xmlns = 'http://www.w3.org/2000/svg';
    var svgElem = document.createElementNS (xmlns, 'svg');
    svgElem.setAttributeNS (null, 'viewBox', '0 0 ' + svgWidth + ' ' + svgHeight);
    svgElem.setAttributeNS (null, 'width', svgWidth);
    svgElem.setAttributeNS (null, 'height', svgHeight);
    svgElem.style.display = 'block';

    //create wrapper path
    var g = document.createElementNS (xmlns, 'g');
    svgElem.appendChild (g);

    //create grid
    var data = this.createGridSrc();
    var count = 0;
    for (var i = data.length - 1; i >= 0; i--) {
      for (var ii = data[i].length - 1; ii >= 0; ii--) {

        // This svgHeight and svgWidth subtraction here flips the image over
        // perhaps this should be accomplished elsewhere.
        var y = svgHeight - (cellHeight * i) - cellOffset;
        var x = svgWidth - (cellWidth * ii) - cellOffset;

        var cell = document.createElementNS (xmlns, 'circle');
        var template = data[i][ii];

        // Machine has averaged the amount of fill per pixel
        // from 0 - 255, so you can filter just the red pixels like this
        // over a certain strength.
        if (template[0] > 10 ) {
          cell.setAttributeNS (null, 'fill', '#ff0000');
          // Consider stashing refs to these in this.groups['red'] or something
          // similar
        } else {
          cell.setAttributeNS (null, 'fill', 'none');
        }

        cell.setAttributeNS (null, 'stroke', '#000000');
        cell.setAttributeNS (null, 'stroke-miterlimit', '#10');
        cell.setAttributeNS (null, 'cx', x);
        cell.setAttributeNS (null, 'cy', y);
        cell.setAttributeNS (null, 'r', radius);

        g.appendChild (cell);
      }
    }
    return svgElem;
  }
  return gm_;
}

И затем в main.js

var wrapper = document.getElementById('wrapper');

var mySVG = document.getElementById('alaska').outerHTML;

mySVG = mySVG.slice(0, 4) + ' height="100" ' + mySVG.slice(4);

// Create a Data URI based on the #alaska element.
var mySrc = 'data:image/svg+xml;base64,' + window.btoa(mySVG);

// Create a new image to do our resizing and capture our pixel data from.
var source = new Image();
source.onload = function() {

  var svgRasterStage = document.createElement('canvas');
  svgRasterStage.width = 1000;
  svgRasterStage.height = 1000;

  svgRasterStage.classList.add('hidden');

  // You may not need this at all, I didn't test it.
  wrapper.appendChild(svgRasterStage);

  // Get drawing context for the Canvas
  var svgRasterStageContext = svgRasterStage.getContext('2d');

  // Draw the SVG to the stage.
  svgRasterStageContext.drawImage(source, 0, 0);

  // We can now get array of rgba pixels all concatinated together:
  //    [ r, g, b, a, r, g, b, a,  (...)  r, g, b, a, r, g, b, a]
  var rgbaConcat = svgRasterStageContext.getImageData(0, 0, 100, 100).data;

  // Which sucks, so here a way to convert them to pixels that we can 
  // use with GridManager.createSvgGrid.
  var pixels = [];
  var count = 0;

  // NOTE: this is a for with a weird step: i=i-4. i-4 is an infinte loop.
  // anything else just jumbles the pixels.
  for (var i = rgbaConcat.length - 1; i >= 0; i=i-4) {
    var r = rgbaConcat[i - 0];
    var g = rgbaConcat[i - 1];
    var b = rgbaConcat[i - 2];
    var a = rgbaConcat[i - 3];
    pixels.push([r, g, b, a]);
  }

  // We create our GridManager (finally).
  var gm = new GridManager({
    'gridWidth': 100,
    'gridHeight': 100,
    'dataSrc': pixels
  });

  // And let her rip!
  wrapper.appendChild(gm.createSvgGrid());
}

Ответ 2

Я попытался быстро решить эту проблему и сделал некоторые исследования, но все еще не завершен/завершен (вы можете завершить его в своей финальной реализации).

Вам нужно иметь функцию, которая проверяет Если точка находится внутри пути. Я нашел 2 библиотеки в JS: Raphael и SnapSVG.

Я разветкил и отредактировал свой JSFiddle и быстро попытался его решить. Моя первая попытка заключалась в функции SnapSVG, но она вернула мне меньше ожидаемого результата, чем Функция Рафаэля.

Откройте скрипку и проверьте: https://jsfiddle.net/edmundo096/7sjLb956/4/. Остерегайтесь того, что масштаб 2 замедлит ваш браузер, хотя я использовал его, чтобы увидеть правильный результат, но для просмотра чего-то потребуется время (мобильные браузеры могут повесить трубку).

var alaska = $('#alaska');
var grid = $('#grid');
var path =  alaska.find('path').first().attr('d');

grid.children().each(function(){
    var circle = $(this);
    var scale = 2;

    // SnapSVG version: var isInside = Snap.path.isPointInside(path, 
    var isInside = Raphael.isPointInsidePath(path, 
                        circle.attr('cx') * scale, 
                        circle.attr('cy') * scale);
    if (isInside) {
        circle.attr('fill', 'blue');
    }
});

(Я использовал jQuery и 2 внешних ресурса: Raphael и SnapSvg из CloudFare CDN)

Как вы можете видеть на следующем изображении, он генерирует своего рода точечную карту, но все же вам нужно исправить отображение, размещение, масштаб и т.д. Пути.

Результат быстрой проверки Raphael:

Быстрый результат функции Рафаэля

Первый быстрый результат SnapSVG: Быстрый результат функции SnapSvg

Вы можете кэшировать свой результат; сохранить приведенную карту в объекте карты JSON, а затем загрузить ее отдельно , чтобы сохранить расчет времени из этого сложного пути.

Надеюсь, это поможет вам.

Ответ 3

Вы можете взять координаты центра круга и использовать что-то вроде функции Raphael isPointInsidePath(), чтобы проверить, находится ли она внутри пути карты.

http://raphaeljs.com/reference.html#Raphael.isPointInsidePath