UNPKG

@canterbury-air-patrol/sar-search-patterns

Version:
411 lines (407 loc) 16.5 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // index.ts var index_exports = {}; __export(index_exports, { CreepingLineAheadSearch: () => CreepingLineAheadSearch, ExpandingBoxSearch: () => ExpandingBoxSearch, SearchConfiguration: () => SearchConfiguration, SearchDisplay: () => SearchDisplay, SearchLeg: () => SearchLeg, SearchPattern: () => SearchPattern, SectorSearch: () => SectorSearch }); module.exports = __toCommonJS(index_exports); // react.tsx var import_react = __toESM(require("react")); var import_react_bootstrap = require("react-bootstrap"); // sar-search-patterns.ts var xy = class { constructor(x, y) { this.x = x; this.y = y; } }; var SearchLeg = class { constructor(from, to, distance, bearing) { this.from = from; this.to = to; this.bearing = bearing; this.distance = distance; } }; var SearchPattern = class { constructor(sweepWidth) { this.sweepWidth = sweepWidth; this.searchLegs = []; this.currentLeg = 1; } get leg() { return this.searchLegs[this.currentLeg - 1]; } get complete() { return this.currentLeg > this.searchLegs.length; } get length() { let length = 0; for (const legIdx in this.searchLegs) { const tmpLeg = this.searchLegs[legIdx]; length += tmpLeg.distance; } return length; } getLegs() { return this.searchLegs; } nextLeg() { this.currentLeg++; } uniqueKey() { return `${this.searchType}-${this.sweepWidth}-${this.searchLegs}-${this.currentLeg}`; } }; function move(from, direction, distance) { const to = { x: 0, y: 0 }; const rads = direction * Math.PI / 180; to.x = from.x + Math.sin(rads) * distance; to.y = from.y + Math.cos(rads) * distance; return to; } var SectorSearch = class extends SearchPattern { constructor(sweepWidth, multiplier, iterations, startingDirection) { super(sweepWidth); this.searchType = "sector"; this.startingDirection = Number(startingDirection); if (this.startingDirection === void 0) { this.startingDirection = 0; } this.multiplier = multiplier; if (this.multiplier <= 0) { this.multiplier = 1; } if (this.multiplier > 3) { this.multiplier = 3; } this.iterations = iterations; if (!(this.iterations === 1 || this.iterations === 2 || this.iterations === 3)) { this.iterations = 1; } this.generateSearchLegs(); } generateSearchLegs() { this.searchLegs = []; let currentBearing = this.startingDirection; let lastPoint = { x: 0, y: 0 }; const legLength = this.sweepWidth * this.multiplier; for (let i = 1; i < 9 * this.iterations + 1; i++) { const from = lastPoint; let to = { x: 0, y: 0 }; if (i % 3 !== 0) { to = move(from, currentBearing, legLength); } this.searchLegs.push(new SearchLeg(from, to, legLength, currentBearing)); if (i % 3 === 0) { if (i % 9 === 0) { currentBearing = (currentBearing + 30) % 360; } } else { currentBearing = (currentBearing + 120) % 360; } lastPoint = to; } } uniqueKey() { return `${this.searchType}-${this.sweepWidth}-${this.searchLegs}-${this.currentLeg}-${this.startingDirection}-${this.multiplier}-${this.iterations}`; } }; var ExpandingBoxSearch = class extends SearchPattern { constructor(sweepWidth, iterations, startingDirection) { super(sweepWidth); this.searchType = "expandingbox"; this.iterations = iterations; this.startingDirection = Number(startingDirection); this.generateSearchLegs(); } generateSearchLegs() { this.searchLegs = []; let direction = this.startingDirection; let from = { x: 0, y: 0 }; for (let i = 0; i < this.iterations * 4; i++) { const legLength = this.sweepWidth * (1 + Math.round((i - 1) / 2)); const to = move(from, direction, legLength); this.searchLegs.push(new SearchLeg(from, to, this.sweepWidth * (1 + Math.round((i - 1) / 2)), direction)); direction = (direction + 90) % 360; from = to; } } uniqueKey() { return `${this.searchType}-${this.sweepWidth}-${this.searchLegs}-${this.currentLeg}-${this.startingDirection}-${this.iterations}`; } }; var CreepingLineAheadSearch = class extends SearchPattern { constructor(sweepWidth, legLength, legs, progressDirection) { super(sweepWidth); this.searchType = "creepingline"; this.legLength = Number(legLength); this.legs = Number(legs); this.progressDirection = Number(progressDirection); this.generateSearchLegs(); } generateSearchLegs() { this.searchLegs = []; const baseNear = new xy(0, 0); const baseFar = move(baseNear, this.progressDirection + 90, this.legLength); for (let i = 1; i < this.legs * 2; i++) { let direction = this.progressDirection; let from; let to; let distance; const leg = Math.round(i / 2); if (i % 4 === 0) { from = move(baseNear, this.progressDirection, this.sweepWidth * leg); to = move(baseNear, this.progressDirection, this.sweepWidth * (leg + 1)); distance = this.sweepWidth; } else if (i % 4 === 1) { from = move(baseNear, this.progressDirection, this.sweepWidth * leg); to = move(baseFar, this.progressDirection, this.sweepWidth * leg); direction = (direction + 90) % 360; distance = this.legLength; } else if (i % 4 === 2) { from = move(baseFar, this.progressDirection, this.sweepWidth * leg); to = move(baseFar, this.progressDirection, this.sweepWidth * (leg + 1)); distance = this.sweepWidth; } else { from = move(baseFar, this.progressDirection, this.sweepWidth * leg); to = move(baseNear, this.progressDirection, this.sweepWidth * leg); direction = (direction + 270) % 360; distance = this.legLength; } this.searchLegs.push(new SearchLeg(from, to, distance, direction)); } } uniqueKey() { return `${this.searchType}-${this.sweepWidth}-${this.searchLegs}-${this.currentLeg}-${this.legLength}-${this.legs}-${this.progressDirection}`; } }; // react.tsx var SearchDisplay = class extends import_react.default.Component { constructor(props) { super(props); this.state = { search: this.props.search }; this.canvasRef = import_react.default.createRef(); this.minX = 0; this.minY = 0; this.maxX = 0; this.maxY = 0; } updateX(x) { if (x < this.minX) { this.minX = x; } if (x > this.maxX) { this.maxX = x; } } updateY(y) { if (y < this.minY) { this.minY = y; } if (y > this.maxY) { this.maxY = y; } } determineXYRange(searchLegs) { this.minX = 0; this.minY = 0; this.maxX = 0; this.maxY = 0; for (const idx in searchLegs) { const leg = searchLegs[idx]; this.updateX(leg.from.x); this.updateY(leg.from.y); this.updateX(leg.to.x); this.updateY(leg.to.y); } return [Math.abs(this.maxX - this.minX), Math.abs(this.maxY - this.minY)]; } drawSearch(canvas) { const ctx = canvas.getContext("2d"); if (ctx === null) { return; } const searchLegs = this.state.search.getLegs(); const range = this.determineXYRange(searchLegs); const offsetX = Math.abs(this.minX) + 10; const offsetY = Math.abs(this.maxY) + 10; range[0] += 20; range[1] += 20; const scaleX = canvas.width / range[0]; const scaleY = canvas.height / range[1]; let scale = scaleX; if (scaleY < scaleX) { scale = scaleY; } ctx.scale(scale, scale); for (const idx in searchLegs) { const leg = searchLegs[idx]; ctx.beginPath(); ctx.lineWidth = 5; if (this.state.search.leg === leg) { ctx.strokeStyle = "orange"; } else { ctx.strokeStyle = "grey"; } ctx.moveTo(offsetX + leg.from.x, offsetY - leg.from.y); ctx.lineTo(offsetX + leg.to.x, offsetY - leg.to.y); ctx.stroke(); } } componentDidMount() { const canvas = this.canvasRef.current; if (canvas !== null) { this.drawSearch(canvas); } } render() { return /* @__PURE__ */ import_react.default.createElement("canvas", { ref: this.canvasRef, width: 600, height: 600 }); } }; function constructSearch(values) { if (values.searchType === "sector") { return new SectorSearch(values.sweepWidth, values.multiplier, values.iterations, values.initialDirection); } if (values.searchType === "expandingbox") { return new ExpandingBoxSearch(values.sweepWidth, values.iterations, values.initialDirection); } if (values.searchType === "creepingline") { return new CreepingLineAheadSearch(values.sweepWidth, values.legLength, values.iterations, values.initialDirection); } } var SearchConfiguration = class extends import_react.default.Component { constructor(props) { super(props); this.handleStateUpdate = this.handleStateUpdate.bind(this); this.handleChangeSearch = this.handleChangeSearch.bind(this); this.handleChangeSweepWidth = this.handleChangeSweepWidth.bind(this); this.handleChangeMultiplier = this.handleChangeMultiplier.bind(this); this.handleChangeLegLength = this.handleChangeLegLength.bind(this); this.handleChangeIterations = this.handleChangeIterations.bind(this); this.handleChangeDirection = this.handleChangeDirection.bind(this); this.state = { searchType: "sector", sweepWidth: 200, legLength: 1e3, iterations: 1, initialDirection: 0, multiplier: 1 }; } handleStateUpdate() { if (this.props.updateSearch !== void 0) { this.props.updateSearch(constructSearch(this.state)); } } handleChangeSearch(event) { const target = event.target; const value = target.value; this.setState({ searchType: value }, this.handleStateUpdate); } handleChangeSweepWidth(event) { const target = event.target; const value = Number(target.value); this.setState({ sweepWidth: value }, this.handleStateUpdate); } handleChangeLegLength(event) { const target = event.target; const value = Number(target.value); this.setState({ legLength: value }, this.handleStateUpdate); } handleChangeMultiplier(event) { const target = event.target; const value = Number(target.value); this.setState({ multiplier: value }, this.handleStateUpdate); } handleChangeIterations(event) { const target = event.target; const value = Number(target.value); this.setState({ iterations: value }, this.handleStateUpdate); } handleChangeDirection(event) { const target = event.target; const value = Number(target.value); this.setState({ initialDirection: value }, this.handleStateUpdate); } render() { const labels = []; const inputs = []; if (this.state.searchType === "sector" || this.state.searchType === "expandingbox") { labels.push(/* @__PURE__ */ import_react.default.createElement("td", { key: "iterations" }, "Iterations")); inputs.push( /* @__PURE__ */ import_react.default.createElement("td", { key: "iterations" }, /* @__PURE__ */ import_react.default.createElement(import_react_bootstrap.Form.Control, { type: "number", onChange: this.handleChangeIterations, value: this.state.iterations })) ); if (this.state.searchType === "sector") { labels.push(/* @__PURE__ */ import_react.default.createElement("td", { key: "multiplier" }, "Multiplier")); inputs.push( /* @__PURE__ */ import_react.default.createElement("td", { key: "multiplier" }, /* @__PURE__ */ import_react.default.createElement(import_react_bootstrap.Form.Control, { type: "number", onChange: this.handleChangeMultiplier, value: this.state.multiplier })) ); } labels.push(/* @__PURE__ */ import_react.default.createElement("td", { key: "direction" }, "Initial Direction")); inputs.push( /* @__PURE__ */ import_react.default.createElement("td", { key: "direction" }, /* @__PURE__ */ import_react.default.createElement(import_react_bootstrap.Form.Control, { type: "number", onChange: this.handleChangeDirection, value: this.state.initialDirection })) ); } else if (this.state.searchType === "creepingline") { labels.push(/* @__PURE__ */ import_react.default.createElement("td", { key: "legs" }, "Leg Length")); labels.push(/* @__PURE__ */ import_react.default.createElement("td", { key: "iterations" }, "Legs")); labels.push(/* @__PURE__ */ import_react.default.createElement("td", { key: "direction" }, "Progress Direction")); inputs.push( /* @__PURE__ */ import_react.default.createElement("td", { key: "legs" }, /* @__PURE__ */ import_react.default.createElement(import_react_bootstrap.Form.Control, { type: "number", onChange: this.handleChangeLegLength, value: this.state.legLength })) ); inputs.push( /* @__PURE__ */ import_react.default.createElement("td", { key: "iterations" }, /* @__PURE__ */ import_react.default.createElement(import_react_bootstrap.Form.Control, { type: "number", onChange: this.handleChangeIterations, value: this.state.iterations })) ); inputs.push( /* @__PURE__ */ import_react.default.createElement("td", { key: "direction" }, /* @__PURE__ */ import_react.default.createElement(import_react_bootstrap.Form.Control, { type: "number", onChange: this.handleChangeDirection, value: this.state.initialDirection })) ); } return /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement(import_react_bootstrap.Table, null, /* @__PURE__ */ import_react.default.createElement("thead", null, /* @__PURE__ */ import_react.default.createElement("tr", null, /* @__PURE__ */ import_react.default.createElement("td", null, "Search Type"), /* @__PURE__ */ import_react.default.createElement("td", null, "Sweep Width"), labels)), /* @__PURE__ */ import_react.default.createElement("tbody", null, /* @__PURE__ */ import_react.default.createElement("tr", null, /* @__PURE__ */ import_react.default.createElement("td", null, /* @__PURE__ */ import_react.default.createElement(import_react_bootstrap.Form.Select, { onChange: this.handleChangeSearch }, /* @__PURE__ */ import_react.default.createElement("option", { value: "sector" }, "Sector"), /* @__PURE__ */ import_react.default.createElement("option", { value: "expandingbox" }, "Expanding Box"), /* @__PURE__ */ import_react.default.createElement("option", { value: "creepingline" }, "Creeping Line"))), /* @__PURE__ */ import_react.default.createElement("td", null, /* @__PURE__ */ import_react.default.createElement(import_react_bootstrap.Form.Control, { type: "number", onChange: this.handleChangeSweepWidth, value: this.state.sweepWidth })), inputs)))); } }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { CreepingLineAheadSearch, ExpandingBoxSearch, SearchConfiguration, SearchDisplay, SearchLeg, SearchPattern, SectorSearch }); //# sourceMappingURL=index.js.map