Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Firefox/devtools/client/shared/test/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 26 kB image not shown  

Quelle  highlighter-test-actor.js   Sprache: JAVA

 
/* Any copyright is dedicated to the Public Domain.
 http://creativecommons.org/publicdomain/zero/1.0/ */


/* exported HighlighterTestActor, HighlighterTestFront */

"use strict";

// A helper actor for testing highlighters.
// ⚠️ This should only be used for getting data for objects using CanvasFrameAnonymousContentHelper,
// that we can't get directly from tests.
const {
  getRect,
  getAdjustedQuads,
} = require("resource://devtools/shared/layout/utils.js");

// Set up a dummy environment so that EventUtils works. We need to be careful to
// pass a window object into each EventUtils method we call rather than having
// it rely on the |window| global.
const EventUtils = {};
EventUtils.window = {};
EventUtils.parent = {};
/* eslint-disable camelcase */
EventUtils._EU_Ci = Ci;
EventUtils._EU_Cc = Cc;
/* eslint-disable camelcase */
Services.scriptloader.loadSubScript(
  "chrome://mochikit/content/tests/SimpleTest/EventUtils.js",
  EventUtils
);

// We're an actor so we don't run in the browser test environment, so
// we need to import TestUtils manually despite what the linter thinks.
// eslint-disable-next-line mozilla/no-redeclare-with-import-autofix
const { TestUtils } = ChromeUtils.importESModule(
  "resource://testing-common/TestUtils.sys.mjs"
);

const protocol = require("resource://devtools/shared/protocol.js");
const { Arg, RetVal } = protocol;

const dumpn = msg => {
  dump(msg + "\n");
};

/**
 * Get the instance of CanvasFrameAnonymousContentHelper used by a given
 * highlighter actor.
 * The instance provides methods to get/set attributes/text/style on nodes of
 * the highlighter, inserted into the nsCanvasFrame.
 * @see /devtools/server/actors/highlighters.js
 * @param {String} actorID
 */

function getHighlighterCanvasFrameHelper(conn, actorID) {
  // Retrieve the CustomHighlighterActor by its actorID:
  const actor = conn.getActor(actorID);
  if (!actor) {
    return null;
  }

  // Retrieve the sub class instance specific to each highlighter type:
  let highlighter = actor.instance;

  // SelectorHighlighter and TabbingOrderHighlighter can hold multiple highlighters.
  // For now, only retrieve the first highlighter.
  if (
    highlighter._highlighters &&
    Array.isArray(highlighter._highlighters) &&
    highlighter._highlighters.length
  ) {
    highlighter = highlighter._highlighters[0];
  }

  // Now, `highlighter` should be a final highlighter class, exposing
  // `CanvasFrameAnonymousContentHelper` via a `markup` attribute.
  if (highlighter.markup) {
    return highlighter.markup;
  }

  // Here we didn't find any highlighter; it can happen if the actor is a
  // FontsHighlighter (which does not use a CanvasFrameAnonymousContentHelper).
  return null;
}

var highlighterTestSpec = protocol.generateActorSpec({
  typeName: "highlighterTest",

  events: {
    "highlighter-updated": {},
  },

  methods: {
    getHighlighterAttribute: {
      request: {
        nodeID: Arg(0, "string"),
        name: Arg(1, "string"),
        actorID: Arg(2, "string"),
      },
      response: {
        value: RetVal("string"),
      },
    },
    getHighlighterBoundingClientRect: {
      request: {
        nodeID: Arg(0, "string"),
        actorID: Arg(1, "string"),
      },
      response: {
        value: RetVal("json"),
      },
    },
    getHighlighterComputedStyle: {
      request: {
        nodeID: Arg(0, "string"),
        property: Arg(1, "string"),
        actorID: Arg(2, "string"),
      },
      response: {
        value: RetVal("string"),
      },
    },
    getHighlighterNodeTextContent: {
      request: {
        nodeID: Arg(0, "string"),
        actorID: Arg(1, "string"),
      },
      response: {
        value: RetVal("string"),
      },
    },
    getSelectorHighlighterBoxNb: {
      request: {
        highlighter: Arg(0, "string"),
      },
      response: {
        value: RetVal("number"),
      },
    },
    changeHighlightedNodeWaitForUpdate: {
      request: {
        name: Arg(0, "string"),
        value: Arg(1, "string"),
        actorID: Arg(2, "string"),
      },
      response: {},
    },
    registerOneTimeHighlighterUpdate: {
      request: {
        actorID: Arg(0, "string"),
      },
      response: {},
    },
    getNodeRect: {
      request: {
        selector: Arg(0, "string"),
      },
      response: {
        value: RetVal("json"),
      },
    },
    getTextNodeRect: {
      request: {
        parentSelector: Arg(0, "string"),
        childNodeIndex: Arg(1, "number"),
      },
      response: {
        value: RetVal("json"),
      },
    },
    isPausedDebuggerOverlayVisible: {
      request: {},
      response: {
        value: RetVal("boolean"),
      },
    },
    clickPausedDebuggerOverlayButton: {
      request: {
        id: Arg(0, "string"),
      },
      response: {},
    },
    isEyeDropperVisible: {
      request: {},
      response: {
        value: RetVal("boolean"),
      },
    },
    getEyeDropperElementAttribute: {
      request: {
        elementId: Arg(0, "string"),
        attributeName: Arg(1, "string"),
      },
      response: {
        value: RetVal("string"),
      },
    },
    getEyeDropperColorValue: {
      request: {},
      response: {
        value: RetVal("string"),
      },
    },
    getTabbingOrderHighlighterData: {
      request: {},
      response: {
        value: RetVal("json"),
      },
    },
  },
});

class HighlighterTestActor extends protocol.Actor {
  constructor(conn, targetActor) {
    super(conn, highlighterTestSpec);

    this.targetActor = targetActor;
  }

  get content() {
    return this.targetActor.window;
  }

  /**
   * Helper to retrieve a DOM element.
   * @param {string | array} selector Either a regular selector string
   *   or a selector array. If an array, each item, except the last one
   *   are considered matching an iframe, so that we can query element
   *   within deep iframes.
   */

  _querySelector(selector) {
    let document = this.content.document;
    if (Array.isArray(selector)) {
      const fullSelector = selector.join(" >> ");
      while (selector.length > 1) {
        const str = selector.shift();
        const iframe = document.querySelector(str);
        if (!iframe) {
          throw new Error(
            'Unable to find element with selector "' +
              str +
              '"' +
              " (full selector:" +
              fullSelector +
              ")"
          );
        }
        if (!iframe.contentWindow) {
          throw new Error(
            "Iframe selector doesn't target an iframe \"" +
              str +
              '"' +
              " (full selector:" +
              fullSelector +
              ")"
          );
        }
        document = iframe.contentWindow.document;
      }
      selector = selector.shift();
    }
    const node = document.querySelector(selector);
    if (!node) {
      throw new Error(
        'Unable to find element with selector "' + selector + '"'
      );
    }
    return node;
  }

  /**
   * Get a value for a given attribute name, on one of the elements of the box
   * model highlighter, given its ID.
   * @param {String} nodeID The full ID of the element to get the attribute for
   * @param {String} name The name of the attribute to get
   * @param {String} actorID The highlighter actor ID
   * @return {String} The value, if found, null otherwise
   */

  getHighlighterAttribute(nodeID, name, actorID) {
    const helper = getHighlighterCanvasFrameHelper(this.conn, actorID);

    if (!helper) {
      throw new Error(`Highlighter not found`);
    }

    return helper.getAttributeForElement(nodeID, name);
  }

  /**
   * Get the bounding client rect for an highlighter element, given its ID.
   *
   * @param {String} nodeID The full ID of the element to get the DOMRect for
   * @param {String} actorID The highlighter actor ID
   * @return {DOMRect} The value, if found, null otherwise
   */

  getHighlighterBoundingClientRect(nodeID, actorID) {
    const helper = getHighlighterCanvasFrameHelper(this.conn, actorID);

    if (!helper) {
      throw new Error(`Highlighter not found`);
    }

    return helper.getBoundingClientRect(nodeID);
  }

  /**
   * Get the computed style for a given property, on one of the elements of the
   * box model highlighter, given its ID.
   * @param {String} nodeID The full ID of the element to get the attribute for
   * @param {String} property The name of the property
   * @param {String} actorID The highlighter actor ID
   * @return {String} The computed style of the property
   */

  getHighlighterComputedStyle(nodeID, property, actorID) {
    const helper = getHighlighterCanvasFrameHelper(this.conn, actorID);

    if (!helper) {
      throw new Error(`Highlighter not found`);
    }

    return helper.getElement(nodeID).computedStyle.getPropertyValue(property);
  }

  /**
   * Get the textcontent of one of the elements of the box model highlighter,
   * given its ID.
   * @param {String} nodeID The full ID of the element to get the attribute for
   * @param {String} actorID The highlighter actor ID
   * @return {String} The textcontent value
   */

  getHighlighterNodeTextContent(nodeID, actorID) {
    let value;
    const helper = getHighlighterCanvasFrameHelper(this.conn, actorID);
    if (helper) {
      value = helper.getTextContentForElement(nodeID);
    }
    return value;
  }

  /**
   * Get the number of box-model highlighters created by the SelectorHighlighter
   * @param {String} actorID The highlighter actor ID
   * @return {Number} The number of box-model highlighters created, or null if the
   * SelectorHighlighter was not found.
   */

  getSelectorHighlighterBoxNb(actorID) {
    const highlighter = this.conn.getActor(actorID);
    const { _highlighter: h } = highlighter;
    if (!h || !h._highlighters) {
      return null;
    }
    return h._highlighters.length;
  }

  /**
   * Subscribe to the box-model highlighter's update event, modify an attribute of
   * the currently highlighted node and send a message when the highlighter has
   * updated.
   * @param {String} the name of the attribute to be changed
   * @param {String} the new value for the attribute
   * @param {String} actorID The highlighter actor ID
   */

  changeHighlightedNodeWaitForUpdate(name, value, actorID) {
    return new Promise(resolve => {
      const highlighter = this.conn.getActor(actorID);
      const { _highlighter: h } = highlighter;

      h.once("updated", resolve);

      h.currentNode.setAttribute(name, value);
    });
  }

  /**
   * Register a one-time "updated" event listener.
   * The method does not wait for the "updated" event itself so the response can be sent
   * back and the client would know the event listener is properly set.
   * A separate event, "highlighter-updated", will be emitted when the highlighter updates.
   *
   * @param {String} actorID The highlighter actor ID
   */

  registerOneTimeHighlighterUpdate(actorID) {
    const { _highlighter } = this.conn.getActor(actorID);
    _highlighter.once("updated").then(() => this.emit("highlighter-updated"));

    // Return directly so the client knows the event listener is set
  }

  async getNodeRect(selector) {
    const node = this._querySelector(selector);
    return getRect(this.content, node, this.content);
  }

  async getTextNodeRect(parentSelector, childNodeIndex) {
    const parentNode = this._querySelector(parentSelector);
    const node = parentNode.childNodes[childNodeIndex];
    return getAdjustedQuads(this.content, node)[0].bounds;
  }

  /**
   * @returns {PausedDebuggerOverlay} The paused overlay instance
   */

  _getPausedDebuggerOverlay() {
    // We use `_pauseOverlay` since it's the cached value; `pauseOverlay` is a getter that
    // will create the overlay when called (if it does not exist yet).
    return this.targetActor?.threadActor?._pauseOverlay;
  }

  isPausedDebuggerOverlayVisible() {
    const pauseOverlay = this._getPausedDebuggerOverlay();
    if (!pauseOverlay) {
      return false;
    }

    const root = pauseOverlay.getElement("root");
    const toolbar = pauseOverlay.getElement("toolbar");

    return (
      root.getAttribute("hidden") !== "true" &&
      root.getAttribute("overlay") == "true" &&
      toolbar.getAttribute("hidden") !== "true" &&
      !!toolbar.getTextContent()
    );
  }

  /**
   * Simulates a click on a button of the debugger pause overlay.
   *
   * @param {String} id: The id of the element (e.g. "paused-dbg-resume-button").
   */

  async clickPausedDebuggerOverlayButton(id) {
    const pauseOverlay = this._getPausedDebuggerOverlay();
    if (!pauseOverlay) {
      return;
    }

    // Because the highlighter markup elements live inside an anonymous content frame which
    // does not expose an API to dispatch events to them, we can't directly dispatch
    // events to the nodes themselves.
    // We're directly calling `handleEvent` on the pause overlay, which is the mouse events
    // listener callback on the overlay.
    pauseOverlay.handleEvent({ type: "mousedown", target: { id } });
  }

  /**
   * @returns {EyeDropper}
   */

  _getEyeDropper() {
    const form = this.targetActor.form();
    const inspectorActor = this.conn._getOrCreateActor(form.inspectorActor);
    return inspectorActor?._eyeDropper;
  }

  isEyeDropperVisible() {
    const eyeDropper = this._getEyeDropper();
    if (!eyeDropper) {
      return false;
    }

    return eyeDropper.getElement("root").getAttribute("hidden") !== "true";
  }

  getEyeDropperElementAttribute(elementId, attributeName) {
    const eyeDropper = this._getEyeDropper();
    if (!eyeDropper) {
      return null;
    }

    return eyeDropper.getElement(elementId).getAttribute(attributeName);
  }

  async getEyeDropperColorValue() {
    const eyeDropper = this._getEyeDropper();
    if (!eyeDropper) {
      return null;
    }

    // It might happen that while the eyedropper isn't hidden anymore, the color-value
    // is not set yet.
    const color = await TestUtils.waitForCondition(() => {
      const colorValueElement = eyeDropper.getElement("color-value");
      const textContent = colorValueElement.getTextContent();
      return textContent;
    }, "Couldn't get a non-empty text content for the color-value element");

    return color;
  }

  /**
   * Get the TabbingOrderHighlighter for the associated targetActor
   *
   * @returns {TabbingOrderHighlighter}
   */

  _getTabbingOrderHighlighter() {
    const form = this.targetActor.form();
    const accessibilityActor = this.conn._getOrCreateActor(
      form.accessibilityActor
    );

    if (!accessibilityActor) {
      return null;
    }
    // We use `_tabbingOrderHighlighter` since it's the cached value; `tabbingOrderHighlighter`
    // is a getter that will create the highlighter when called (if it does not exist yet).
    return accessibilityActor.walker?._tabbingOrderHighlighter;
  }

  /**
   * Get a representation of the NodeTabbingOrderHighlighters created by the
   * TabbingOrderHighlighter of a given targetActor.
   *
   * @returns {Array<String>} An array which will contain as many entry as they are
   *          NodeTabbingOrderHighlighters displayed.
   *          Each item will be of the form `nodename[#id]: index`.
   *          For example:
   *          [
   *            `button#top-btn-1 : 1`,
   *            `html : 2`,
   *            `button#iframe-btn-1 : 3`,
   *            `button#iframe-btn-2 : 4`,
   *            `button#top-btn-2 : 5`,
   *          ]
   */

  getTabbingOrderHighlighterData() {
    const highlighter = this._getTabbingOrderHighlighter();
    if (!highlighter) {
      return [];
    }

    const nodeTabbingOrderHighlighters = [
      ...highlighter._highlighter._highlighters.values(),
    ].filter(h => h.getElement("root").getAttribute("hidden") !== "true");

    return nodeTabbingOrderHighlighters.map(h => {
      let nodeStr = h.currentNode.nodeName.toLowerCase();
      if (h.currentNode.id) {
        nodeStr = `${nodeStr}#${h.currentNode.id}`;
      }
      return `${nodeStr} : ${h.getElement("root").getTextContent()}`;
    });
  }
}
exports.HighlighterTestActor = HighlighterTestActor;

class HighlighterTestFront extends protocol.FrontClassWithSpec(
  highlighterTestSpec
) {
  constructor(client, targetFront, parentFront) {
    super(client, targetFront, parentFront);
    this.formAttributeName = "highlighterTestActor";
    // The currently active highlighter is obtained by calling a custom getter
    // provided manually after requesting TestFront. See `getHighlighterTestFront(toolbox)`
    this._highlighter = null;
  }

  /**
   * Override the highlighter getter with a custom method that returns
   * the currently active highlighter instance.
   *
   * @param {Function|Highlighter} _customHighlighterGetter
   */

  set highlighter(_customHighlighterGetter) {
    this._highlighter = _customHighlighterGetter;
  }

  /**
   * The currently active highlighter instance.
   * If there is a custom getter for the highlighter, return its result.
   *
   * @return {Highlighter|null}
   */

  get highlighter() {
    return typeof this._highlighter === "function"
      ? this._highlighter()
      : this._highlighter;
  }

  /* eslint-disable max-len */
  changeHighlightedNodeWaitForUpdate(name, value, highlighter) {
    /* eslint-enable max-len */
    return super.changeHighlightedNodeWaitForUpdate(
      name,
      value,
      (highlighter || this.highlighter).actorID
    );
  }

  /**
   * Get the value of an attribute on one of the highlighter's node.
   * @param {String} nodeID The Id of the node in the highlighter.
   * @param {String} name The name of the attribute.
   * @param {Object} highlighter Optional custom highlighter to target
   * @return {String} value
   */

  getHighlighterNodeAttribute(nodeID, name, highlighter) {
    return this.getHighlighterAttribute(
      nodeID,
      name,
      (highlighter || this.highlighter).actorID
    );
  }

  getHighlighterNodeTextContent(nodeID, highlighter) {
    return super.getHighlighterNodeTextContent(
      nodeID,
      (highlighter || this.highlighter).actorID
    );
  }

  /**
   * Get the computed style of a property on one of the highlighter's node.
   * @param {String} nodeID The Id of the node in the highlighter.
   * @param {String} property The name of the property.
   * @param {Object} highlighter Optional custom highlighter to target
   * @return {String} value
   */

  getHighlighterComputedStyle(nodeID, property, highlighter) {
    return super.getHighlighterComputedStyle(
      nodeID,
      property,
      (highlighter || this.highlighter).actorID
    );
  }

  /**
   * Is the highlighter currently visible on the page?
   */

  async isHighlighting() {
    // Once the highlighter is hidden, the reference to it is lost.
    // Assume it is not highlighting.
    if (!this.highlighter) {
      return false;
    }

    try {
      const hidden = await this.getHighlighterNodeAttribute(
        "box-model-elements",
        "hidden"
      );
      return hidden === null;
    } catch (e) {
      if (e.message.match(/Highlighter not found/)) {
        return false;
      }
      throw e;
    }
  }

  /**
   * Get the current rect of the border region of the box-model highlighter
   */

  async getSimpleBorderRect() {
    const { border } = await this.getBoxModelStatus();
    const { p1, p2, p4 } = border.points;

    return {
      top: p1.y,
      left: p1.x,
      width: p2.x - p1.x,
      height: p4.y - p1.y,
    };
  }

  /**
   * Get the current positions and visibility of the various box-model highlighter
   * elements.
   */

  async getBoxModelStatus() {
    const isVisible = await this.isHighlighting();

    const ret = {
      visible: isVisible,
    };

    for (const region of ["margin""border""padding""content"]) {
      const points = await this._getPointsForRegion(region);
      const visible = await this._isRegionHidden(region);
      ret[region] = { points, visible };
    }

    ret.guides = {};
    for (const guide of ["top""right""bottom""left"]) {
      ret.guides[guide] = await this._getGuideStatus(guide);
    }

    return ret;
  }

  /**
   * Check that the box-model highlighter is currently highlighting the node matching the
   * given selector.
   * @param {String} selector
   * @return {Boolean}
   */

  async assertHighlightedNode(selector) {
    const rect = await this.getNodeRect(selector);
    return this.isNodeRectHighlighted(rect);
  }

  /**
   * Check that the box-model highlighter is currently highlighting the text node that can
   * be found at a given index within the list of childNodes of a parent element matching
   * the given selector.
   * @param {String} parentSelector
   * @param {Number} childNodeIndex
   * @return {Boolean}
   */

  async assertHighlightedTextNode(parentSelector, childNodeIndex) {
    const rect = await this.getTextNodeRect(parentSelector, childNodeIndex);
    return this.isNodeRectHighlighted(rect);
  }

  /**
   * Check that the box-model highlighter is currently highlighting the given rect.
   * @param {Object} rect
   * @return {Boolean}
   */

  async isNodeRectHighlighted({ left, top, width, height }) {
    const { visible, border } = await this.getBoxModelStatus();
    let points = border.points;
    if (!visible) {
      return false;
    }

    // Check that the node is within the box model
    const right = left + width;
    const bottom = top + height;

    // Converts points dictionnary into an array
    const list = [];
    for (let i = 1; i <= 4; i++) {
      const p = points["p" + i];
      list.push([p.x, p.y]);
    }
    points = list;

    // Check that each point of the node is within the box model
    return (
      isInside([left, top], points) &&
      isInside([right, top], points) &&
      isInside([right, bottom], points) &&
      isInside([left, bottom], points)
    );
  }

  /**
   * Get the coordinate (points attribute) from one of the polygon elements in the
   * box model highlighter.
   */

  async _getPointsForRegion(region) {
    const d = await this.getHighlighterNodeAttribute(
      "box-model-" + region,
      "d"
    );

    if (!d) {
      return null;
    }

    const polygons = d.match(/M[^M]+/g);
    if (!polygons) {
      return null;
    }

    const points = polygons[0]
      .trim()
      .split(" ")
      .map(i => {
        return i.replace(/M|L/, "").split(",");
      });

    return {
      p1: {
        x: parseFloat(points[0][0]),
        y: parseFloat(points[0][1]),
      },
      p2: {
        x: parseFloat(points[1][0]),
        y: parseFloat(points[1][1]),
      },
      p3: {
        x: parseFloat(points[2][0]),
        y: parseFloat(points[2][1]),
      },
      p4: {
        x: parseFloat(points[3][0]),
        y: parseFloat(points[3][1]),
      },
    };
  }

  /**
   * Is a given region polygon element of the box-model highlighter currently
   * hidden?
   */

  async _isRegionHidden(region) {
    const value = await this.getHighlighterNodeAttribute(
      "box-model-" + region,
      "hidden"
    );
    return value !== null;
  }

  async _getGuideStatus(location) {
    const id = "box-model-guide-" + location;

    const hidden = await this.getHighlighterNodeAttribute(id, "hidden");
    const x1 = await this.getHighlighterNodeAttribute(id, "x1");
    const y1 = await this.getHighlighterNodeAttribute(id, "y1");
    const x2 = await this.getHighlighterNodeAttribute(id, "x2");
    const y2 = await this.getHighlighterNodeAttribute(id, "y2");

    return {
      visible: !hidden,
      x1,
      y1,
      x2,
      y2,
    };
  }

  /**
   * Get the coordinates of the rectangle that is defined by the 4 guides displayed
   * in the toolbox box-model highlighter.
   * @return {Object} Null if at least one guide is hidden. Otherwise an object
   * with p1, p2, p3, p4 properties being {x, y} objects.
   */

  async getGuidesRectangle() {
    const tGuide = await this._getGuideStatus("top");
    const rGuide = await this._getGuideStatus("right");
    const bGuide = await this._getGuideStatus("bottom");
    const lGuide = await this._getGuideStatus("left");

    if (
      !tGuide.visible ||
      !rGuide.visible ||
      !bGuide.visible ||
      !lGuide.visible
    ) {
      return null;
    }

    return {
      p1: { x: lGuide.x1, y: tGuide.y1 },
      p2: { x: +rGuide.x1 + 1, y: tGuide.y1 },
      p3: { x: +rGuide.x1 + 1, y: +bGuide.y1 + 1 },
      p4: { x: lGuide.x1, y: +bGuide.y1 + 1 },
    };
  }

  /**
   * Get the "d" attribute value for one of the box-model highlighter's region
   * <path> elements, and parse it to a list of points.
   * @param {String} region The box model region name.
   * @param {Front} highlighter The front of the highlighter.
   * @return {Object} The object returned has the following form:
   * - d {String} the d attribute value
   * - points {Array} an array of all the polygons defined by the path. Each box
   *   is itself an Array of points, themselves being [x,y] coordinates arrays.
   */

  async getHighlighterRegionPath(region, highlighter) {
    const d = await this.getHighlighterNodeAttribute(
      `box-model-${region}`,
      "d",
      highlighter
    );
    if (!d) {
      return { d: null };
    }

    const polygons = d.match(/M[^M]+/g);
    if (!polygons) {
      return { d };
    }

    const points = [];
    for (const polygon of polygons) {
      points.push(
        polygon
          .trim()
          .split(" ")
          .map(i => {
            return i.replace(/M|L/, "").split(",");
          })
      );
    }

    return { d, points };
  }
}
protocol.registerFront(HighlighterTestFront);
/**
 * Check whether a point is included in a polygon.
 * Taken and tweaked from:
 * https://github.com/iominh/point-in-polygon-extended/blob/master/src/index.js#L30-L85
 * @param {Array} point [x,y] coordinates
 * @param {Array} polygon An array of [x,y] points
 * @return {Boolean}
 */

function isInside(point, polygon) {
  if (polygon.length === 0) {
    return false;
  }

  // Reduce the length of the fractional part because this is likely to cause errors when
  // the point is on the edge of the polygon.
  point = point.map(n => n.toFixed(2));
  polygon = polygon.map(p => p.map(n => n.toFixed(2)));

  const n = polygon.length;
  const newPoints = polygon.slice(0);
  newPoints.push(polygon[0]);
  let wn = 0;

  // loop through all edges of the polygon
  for (let i = 0; i < n; i++) {
    // Accept points on the edges
    const r = isLeft(newPoints[i], newPoints[i + 1], point);
    if (r === 0) {
      return true;
    }
    if (newPoints[i][1] <= point[1]) {
      if (newPoints[i + 1][1] > point[1] && r > 0) {
        wn++;
      }
    } else if (newPoints[i + 1][1] <= point[1] && r < 0) {
      wn--;
    }
  }
  if (wn === 0) {
    dumpn(JSON.stringify(point) + " is outside of " + JSON.stringify(polygon));
  }
  // the point is outside only when this winding number wn===0, otherwise it's inside
  return wn !== 0;
}

function isLeft(p0, p1, p2) {
  const l =
    (p1[0] - p0[0]) * (p2[1] - p0[1]) - (p2[0] - p0[0]) * (p1[1] - p0[1]);
  return l;
}

98%


¤ Dauer der Verarbeitung: 0.11 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.