huge performance improvements
This commit is contained in:
@@ -14,7 +14,7 @@ class BlockInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static showStructureBlockInfo(player) {
|
static showStructureBlockInfo(player) {
|
||||||
const block = Raycaster.getTargetedStructureBlock(player, { isFirst: true, collideWithWorldBlocks: true, useActiveLayer: false });
|
const block = Raycaster.getTargetedStructureBlock(player, { isFirst: true, collideWithWorldBlocks: true, useActiveLayer: true });
|
||||||
if (!block && this.shownToLastTick.has(player.id)) {
|
if (!block && this.shownToLastTick.has(player.id)) {
|
||||||
player.onScreenDisplay.setActionBar({ text: 'Structure:\n§7None' });
|
player.onScreenDisplay.setActionBar({ text: 'Structure:\n§7None' });
|
||||||
this.shownToLastTick.delete(player.id);
|
this.shownToLastTick.delete(player.id);
|
||||||
|
|||||||
@@ -152,7 +152,6 @@ export class InstanceEditForm {
|
|||||||
if (response.canceled)
|
if (response.canceled)
|
||||||
return;
|
return;
|
||||||
this.instance.setVerifierEnabled(response.formValues[0]);
|
this.instance.setVerifierEnabled(response.formValues[0]);
|
||||||
this.instance.setVerifierDistance(response.formValues[1]);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9,7 +9,7 @@ export class InstanceEditFormBuilder {
|
|||||||
const location = instance.getLocation();
|
const location = instance.getLocation();
|
||||||
const form = new ActionFormData()
|
const form = new ActionFormData()
|
||||||
.title(MenuFormBuilder.menuTitle)
|
.title(MenuFormBuilder.menuTitle)
|
||||||
let body = `Instance: §a${instance.name}\n§fStructure: §2${instance.options.getStructureId()}\n`;
|
let body = `Instance: §a${instance.getName()}\n§fStructure: §2${instance.getStructureId()}\n`;
|
||||||
if (instance.hasLocation())
|
if (instance.hasLocation())
|
||||||
body += `§7(${location.location.x} ${location.location.y} ${location.location.z} in ${location.dimensionId})\n`;
|
body += `§7(${location.location.x} ${location.location.y} ${location.location.z} in ${location.dimensionId})\n`;
|
||||||
form.body(body);
|
form.body(body);
|
||||||
@@ -49,7 +49,6 @@ export class InstanceEditFormBuilder {
|
|||||||
return new ModalFormData()
|
return new ModalFormData()
|
||||||
.title(MenuFormBuilder.menuTitle)
|
.title(MenuFormBuilder.menuTitle)
|
||||||
.toggle('Toggle block validation.', instance.options.verifier.isEnabled)
|
.toggle('Toggle block validation.', instance.options.verifier.isEnabled)
|
||||||
.slider('Use the slider to restrict how far from players block validation occurs. Use 0 for no restriction.', 0, 10, 1, instance.verifier.trackPlayerDistance)
|
|
||||||
.submitButton('§aApply');
|
.submitButton('§aApply');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Vector } from "../lib/Vector";
|
import { Vector } from "../lib/Vector";
|
||||||
|
import { world } from "@minecraft/server";
|
||||||
|
|
||||||
export class InstanceOptions {
|
export class InstanceOptions {
|
||||||
instanceName = void 0;
|
instanceName = void 0;
|
||||||
@@ -13,6 +14,11 @@ export class InstanceOptions {
|
|||||||
intervalOrLifetime: 10
|
intervalOrLifetime: 10
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static getInstanceStrucetureId(instanceName) {
|
||||||
|
const options = new InstanceOptions(instanceName, void 0);
|
||||||
|
return options.structureId;
|
||||||
|
}
|
||||||
|
|
||||||
constructor(instanceName, structureId) {
|
constructor(instanceName, structureId) {
|
||||||
this.instanceName = instanceName;
|
this.instanceName = instanceName;
|
||||||
this.structureId = structureId;
|
this.structureId = structureId;
|
||||||
@@ -43,7 +49,7 @@ export class InstanceOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getDimension() {
|
getDimension() {
|
||||||
return world.getDimension(this.options.dimensionId);
|
return world.getDimension(this.dimensionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
enable() {
|
enable() {
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export class MenuForm {
|
|||||||
async show(jumpToInstance = true) {
|
async show(jumpToInstance = true) {
|
||||||
let instanceName;
|
let instanceName;
|
||||||
if (jumpToInstance) {
|
if (jumpToInstance) {
|
||||||
instanceName = structureCollection.getStructure(this.player.dimension.id, this.player.location, { useActiveLayer: false })?.name;
|
instanceName = structureCollection.getStructure(this.player.dimension.id, this.player.location, { useActiveLayer: false })?.getName();
|
||||||
if (instanceName) {
|
if (instanceName) {
|
||||||
new InstanceEditForm(this.player, instanceName);
|
new InstanceEditForm(this.player, instanceName);
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -39,10 +39,10 @@ export class Structure {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
*getLayerBlocks(y) {
|
*getLayerBlocks(layer) {
|
||||||
for (let x = 0; x < this.#structure.size.x; x++) {
|
for (let x = 0; x < this.#structure.size.x; x++) {
|
||||||
for (let z = 0; z < this.#structure.size.z; z++) {
|
for (let z = 0; z < this.#structure.size.z; z++) {
|
||||||
yield this.getBlock({ x, y, z });
|
yield this.getBlock({ x, y: layer, z });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -52,5 +52,26 @@ export class Structure {
|
|||||||
yield * this.getLayerBlocks(y);
|
yield * this.getLayerBlocks(y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getLayerLocations(layer) {
|
||||||
|
const locations = new Set();
|
||||||
|
for (let x = 0; x < this.#structure.size.x; x++) {
|
||||||
|
for (let z = 0; z < this.#structure.size.z; z++) {
|
||||||
|
locations.add(new Vector(x, layer, z));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return locations;
|
||||||
|
}
|
||||||
|
|
||||||
|
getAllLocations() {
|
||||||
|
const locations = new Set();
|
||||||
|
for (let y = 0; y < this.#structure.size.y; y++) {
|
||||||
|
const layerLocations = this.getLayerLocations(y);
|
||||||
|
for (const location of layerLocations) {
|
||||||
|
locations.add(location);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return locations;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { InstanceOptions } from './InstanceOptions';
|
||||||
import { StructureInstance } from './StructureInstance';
|
import { StructureInstance } from './StructureInstance';
|
||||||
import { world } from '@minecraft/server';
|
import { world } from '@minecraft/server';
|
||||||
|
|
||||||
@@ -8,12 +9,12 @@ class StructureCollection {
|
|||||||
this.structures = {};
|
this.structures = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
loadExistingStructures() {
|
loadExistingInstances() {
|
||||||
world.getDynamicPropertyIds().filter(id => id.startsWith('structOptions:')).forEach(id => {
|
world.getDynamicPropertyIds().filter(id => id.startsWith('instanceOptions:')).forEach(id => {
|
||||||
const instanceName = id.replace('structOptions:', '');
|
const instanceName = id.replace('instanceOptions:', '');
|
||||||
let structureId;
|
let structureId;
|
||||||
try {
|
try {
|
||||||
structureId = StructureInstance.parseOptions(instanceName).structureId;
|
structureId = InstanceOptions.getInstanceStrucetureId(instanceName);
|
||||||
this.structures[instanceName] = new StructureInstance(instanceName, structureId);
|
this.structures[instanceName] = new StructureInstance(instanceName, structureId);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
world.sendMessage(`§c[StrucTool] Error loading structure instance '${instanceName}'. It will be removed.`);
|
world.sendMessage(`§c[StrucTool] Error loading structure instance '${instanceName}'. It will be removed.`);
|
||||||
@@ -95,5 +96,5 @@ class StructureCollection {
|
|||||||
export const structureCollection = new StructureCollection();
|
export const structureCollection = new StructureCollection();
|
||||||
|
|
||||||
world.afterEvents.worldLoad.subscribe(() => {
|
world.afterEvents.worldLoad.subscribe(() => {
|
||||||
structureCollection.loadExistingStructures();
|
structureCollection.loadExistingInstances();
|
||||||
});
|
});
|
||||||
@@ -3,6 +3,7 @@ import { StructureOutliner } from "./StructureOutliner";
|
|||||||
import { StructureVerifier } from "./StructureVerifier";
|
import { StructureVerifier } from "./StructureVerifier";
|
||||||
import { InstanceOptions } from "./InstanceOptions";
|
import { InstanceOptions } from "./InstanceOptions";
|
||||||
import { Structure } from "./Structure";
|
import { Structure } from "./Structure";
|
||||||
|
import { TicksPerSecond } from "@minecraft/server";
|
||||||
|
|
||||||
export class StructureInstance {
|
export class StructureInstance {
|
||||||
options;
|
options;
|
||||||
@@ -18,11 +19,11 @@ export class StructureInstance {
|
|||||||
|
|
||||||
delete() {
|
delete() {
|
||||||
this.disable();
|
this.disable();
|
||||||
this.options.clear();
|
|
||||||
delete this.options;
|
delete this.options;
|
||||||
delete this.structure;
|
delete this.structure;
|
||||||
delete this.outliner;
|
delete this.outliner;
|
||||||
delete this.verifier;
|
delete this.verifier;
|
||||||
|
this.options.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshBox() {
|
refreshBox() {
|
||||||
@@ -36,6 +37,10 @@ export class StructureInstance {
|
|||||||
this.verifier.refresh();
|
this.verifier.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getName() {
|
||||||
|
return this.options.instanceName;
|
||||||
|
}
|
||||||
|
|
||||||
getStructureId() {
|
getStructureId() {
|
||||||
return this.options.structureId;
|
return this.options.structureId;
|
||||||
}
|
}
|
||||||
@@ -111,6 +116,15 @@ export class StructureInstance {
|
|||||||
&& structureLocation.z >= bounds.min.z && structureLocation.z < bounds.max.z;
|
&& structureLocation.z >= bounds.min.z && structureLocation.z < bounds.max.z;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getAllActiveLocations() {
|
||||||
|
if (!this.options.isEnabled)
|
||||||
|
throw new Error(`[StrucTool] Instance '${this.options.instanceName}' is not placed.`);
|
||||||
|
if (this.hasLayerSelected())
|
||||||
|
return this.structure.getLayerLocations(this.getLayer()-1);
|
||||||
|
else
|
||||||
|
return this.structure.getAllLocations();
|
||||||
|
}
|
||||||
|
|
||||||
isEnabled() {
|
isEnabled() {
|
||||||
return this.options.isEnabled;
|
return this.options.isEnabled;
|
||||||
}
|
}
|
||||||
@@ -177,6 +191,12 @@ export class StructureInstance {
|
|||||||
|
|
||||||
setVerifierDistance(distance) {
|
setVerifierDistance(distance) {
|
||||||
this.options.setVerifierDistance(distance);
|
this.options.setVerifierDistance(distance);
|
||||||
|
if (this.options.verifier.trackPlayerDistance === 0) {
|
||||||
|
const bounds = this.getBounds();
|
||||||
|
this.options.verifier.intervalOrLifetime = Math.max(bounds.min.volume(bounds.max) / TicksPerSecond, 2*TicksPerSecond);
|
||||||
|
} else {
|
||||||
|
this.options.verifier.intervalOrLifetime = 10;
|
||||||
|
}
|
||||||
this.verifier.refresh();
|
this.verifier.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ export class StructureStatistics {
|
|||||||
|
|
||||||
getMessage() {
|
getMessage() {
|
||||||
let message = '';
|
let message = '';
|
||||||
message += `§fStatistics for §a${this.instance.name}§f:`;
|
message += `§fStatistics for §a${this.instance.getName()}§f:`;
|
||||||
if (this.instance.hasLayerSelected())
|
if (this.instance.hasLayerSelected())
|
||||||
message += ` §7(layer ${this.instance.getLayer()})`;
|
message += ` §7(layer ${this.instance.getLayer()})`;
|
||||||
message += `\n§7Blocks: §2${this.getNonAirBlocks()}\n`;
|
message += `\n§7Blocks: §2${this.getNonAirBlocks()}\n`;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { BlockVerifier } from "./BlockVerifier";
|
|||||||
import { BlockVerificationLevel } from "./enums/BlockVerificationLevel";
|
import { BlockVerificationLevel } from "./enums/BlockVerificationLevel";
|
||||||
import { BlockVerificationLevelRender } from "./BlockVerificationLevelRender";
|
import { BlockVerificationLevelRender } from "./BlockVerificationLevelRender";
|
||||||
import { system, TicksPerSecond } from "@minecraft/server";
|
import { system, TicksPerSecond } from "@minecraft/server";
|
||||||
|
import { Vector } from "../lib/Vector";
|
||||||
|
|
||||||
const MIN_TRACK_PLAYER_DISTANCE = 0;
|
const MIN_TRACK_PLAYER_DISTANCE = 0;
|
||||||
const MAX_TRACK_PLAYER_DISTANCE = 7;
|
const MAX_TRACK_PLAYER_DISTANCE = 7;
|
||||||
@@ -12,17 +13,20 @@ export class StructureVerifier {
|
|||||||
intervalOrLifetime;
|
intervalOrLifetime;
|
||||||
|
|
||||||
locationsToVerify;
|
locationsToVerify;
|
||||||
blocksToVerify;
|
|
||||||
blockVerificationLevels;
|
blockVerificationLevels;
|
||||||
isBlockPopulationComplete;
|
isLocationPopulationComplete;
|
||||||
isVerificationComplete;
|
isVerificationComplete;
|
||||||
#runner;
|
|
||||||
|
|
||||||
constructor(instance, { isEnabled = false, trackPlayerDistance = 1, intervalOrLifetime = 10 } = {}) {
|
#runner;
|
||||||
|
#verifyJob;
|
||||||
|
#populateJob = {};
|
||||||
|
|
||||||
|
constructor(instance, { isEnabled = false, trackPlayerDistance = 0, intervalOrLifetime = 10 } = {}) {
|
||||||
this.instance = instance;
|
this.instance = instance;
|
||||||
this.intervalOrLifetime = Math.max(intervalOrLifetime, MIN_LIFETIME);
|
this.intervalOrLifetime = Math.max(intervalOrLifetime, MIN_LIFETIME);
|
||||||
this.instance.options.setVerifierEnabled(isEnabled);
|
this.instance.options.setVerifierEnabled(isEnabled);
|
||||||
this.instance.options.setVerifierDistance(trackPlayerDistance);
|
this.instance.options.setVerifierDistance(trackPlayerDistance);
|
||||||
|
this.locationsToVerify = new Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
startContinuousVerification() {
|
startContinuousVerification() {
|
||||||
@@ -50,14 +54,13 @@ export class StructureVerifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getTrackPlayerDistance() {
|
getTrackPlayerDistance() {
|
||||||
return Math.min(MAX_TRACK_PLAYER_DISTANCE, Math.max(MIN_TRACK_PLAYER_DISTANCE, this.instance.options.trackPlayerDistance));
|
return Math.min(MAX_TRACK_PLAYER_DISTANCE, Math.max(MIN_TRACK_PLAYER_DISTANCE, this.instance.options.verifier.trackPlayerDistance));
|
||||||
}
|
}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
this.locationsToVerify = new Set();
|
this.locationsToVerify.clear();
|
||||||
this.blocksToVerify = [];
|
|
||||||
this.blockVerificationLevels = { correctlyAir: 0 };
|
this.blockVerificationLevels = { correctlyAir: 0 };
|
||||||
this.isBlockPopulationComplete = false;
|
this.isLocationPopulationComplete = false;
|
||||||
this.isVerificationComplete = false;
|
this.isVerificationComplete = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,8 +69,10 @@ export class StructureVerifier {
|
|||||||
return;
|
return;
|
||||||
this.init();
|
this.init();
|
||||||
return new Promise(async (resolve) => {
|
return new Promise(async (resolve) => {
|
||||||
await this.populateBlocksToVerify();
|
await this.populateLocationsToVerify();
|
||||||
system.runJob(this.verifyBlocks(this.blocksToVerify, shouldRender));
|
if (this.#verifyJob)
|
||||||
|
system.clearJob(this.#verifyJob);
|
||||||
|
this.verifyJob = system.runJob(this.verifyBlocks(this.locationsToVerify, shouldRender));
|
||||||
const checker = system.runInterval(() => {
|
const checker = system.runInterval(() => {
|
||||||
if (this.isVerificationComplete) {
|
if (this.isVerificationComplete) {
|
||||||
system.clearRun(checker);
|
system.clearRun(checker);
|
||||||
@@ -77,22 +82,23 @@ export class StructureVerifier {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
populateBlocksToVerify() {
|
async populateLocationsToVerify() {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
if (this.getTrackPlayerDistance == 0) {
|
if (this.getTrackPlayerDistance() === 0) {
|
||||||
this.blocksToVerify = this.instance.getAllBlocks();
|
this.locationsToVerify = this.instance.getAllActiveLocations();
|
||||||
resolve();
|
resolve();
|
||||||
|
} else {
|
||||||
|
for (const job of Object.values(this.#populateJob))
|
||||||
|
system.clearJob(job);
|
||||||
|
for (const player of this.instance.getDimension().getPlayers())
|
||||||
|
this.#populateJob[player.id] = system.runJob(this.populateActiveLocationsNearPlayer(player));
|
||||||
|
const checker = system.runInterval(() => {
|
||||||
|
if (this.isLocationPopulationComplete) {
|
||||||
|
system.clearRun(checker);
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
}, 1);
|
||||||
}
|
}
|
||||||
this.locationsToVerify = new Set();
|
|
||||||
for (const player of this.instance.getDimension().getPlayers())
|
|
||||||
system.runJob(this.populateActiveLocationsNearPlayer(player));
|
|
||||||
this.blocksToVerify = this.instance.getBlocks(this.locationsToVerify);
|
|
||||||
const checker = system.runInterval(() => {
|
|
||||||
if (this.isBlockPopulationComplete) {
|
|
||||||
system.clearRun(checker);
|
|
||||||
resolve();
|
|
||||||
}
|
|
||||||
}, 1);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,26 +107,27 @@ export class StructureVerifier {
|
|||||||
for (let x = -distance; x < distance; x++) {
|
for (let x = -distance; x < distance; x++) {
|
||||||
for (let y = -distance; y < distance; y++) {
|
for (let y = -distance; y < distance; y++) {
|
||||||
for (let z = -distance; z < distance; z++) {
|
for (let z = -distance; z < distance; z++) {
|
||||||
const structureLocation = this.instance.toStructureCoords({ x: player.location.x + x, y: player.location.y + y, z: player.location.z + z });
|
const worldLocation = Vector.from(player.location).add(new Vector(x, y, z)).floor();;
|
||||||
|
const structureLocation = this.instance.toStructureCoords(worldLocation);
|
||||||
if (this.instance.isLocationActive(player.dimension.id, structureLocation, { useActiveLayer: true })) {
|
if (this.instance.isLocationActive(player.dimension.id, structureLocation, { useActiveLayer: true })) {
|
||||||
this.locationsToVerify.add({ x: structureLocation.x, y: structureLocation.y, z: structureLocation.z });
|
this.locationsToVerify.add(structureLocation);
|
||||||
yield void 0;
|
|
||||||
}
|
}
|
||||||
|
yield void 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.isBlockPopulationComplete = true;
|
this.isLocationPopulationComplete = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
*verifyBlocks(blocks, shouldRender) {
|
*verifyBlocks(locations, shouldRender) {
|
||||||
for (const block of blocks) {
|
for (const location of locations) {
|
||||||
const verificationLevel = this.verifyBlock(block.location);
|
const verificationLevel = this.verifyBlock(location);
|
||||||
if (verificationLevel === BlockVerificationLevel.Air) {
|
if (verificationLevel === BlockVerificationLevel.Air) {
|
||||||
this.blockVerificationLevels.correctlyAir++;
|
this.blockVerificationLevels.correctlyAir++;
|
||||||
} else {
|
} else {
|
||||||
this.blockVerificationLevels[JSON.stringify(block.location)] = verificationLevel;
|
this.blockVerificationLevels[JSON.stringify(location)] = verificationLevel;
|
||||||
if (shouldRender) {
|
if (shouldRender) {
|
||||||
const dimensionLocation = { dimension: this.instance.getDimension(), location: this.instance.toGlobalCoords(block.location) };
|
const dimensionLocation = { dimension: this.instance.getDimension(), location: this.instance.toGlobalCoords(location) };
|
||||||
new BlockVerificationLevelRender(dimensionLocation, verificationLevel, this.intervalOrLifetime/TicksPerSecond);
|
new BlockVerificationLevelRender(dimensionLocation, verificationLevel, this.intervalOrLifetime/TicksPerSecond);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,10 @@ Vector.multiply = function multiply(vec, num) {
|
|||||||
}
|
}
|
||||||
Vector.isVec3 = function isVec3(vec) { return vec[isVec3Symbol] === true; }
|
Vector.isVec3 = function isVec3(vec) { return vec[isVec3Symbol] === true; }
|
||||||
Vector.floor = function floor(vec) { return { x: Math.floor(vec.x), y: Math.floor(vec.y), z: Math.floor(vec.z), __proto__: Vector.prototype }; }
|
Vector.floor = function floor(vec) { return { x: Math.floor(vec.x), y: Math.floor(vec.y), z: Math.floor(vec.z), __proto__: Vector.prototype }; }
|
||||||
Vector.volume = function volume(a, b) { const [min, max] = Vector.sort(a, b); return (max.x - min.x) * (max.y - min.y) * (max.z - min.z); }
|
Vector.volume = function volume(a, b) {
|
||||||
|
const [min, max] = Vector.sort(a, b);
|
||||||
|
return (max.x - min.x) * (max.y - min.y) * (max.z - min.z);
|
||||||
|
}
|
||||||
Vector.projection = function projection(a, b) { return Vector.multiply(b, Vector.dot(a, b) / ((b.x * b.x + b.y * b.y + b.z * b.z) ** 2)); }
|
Vector.projection = function projection(a, b) { return Vector.multiply(b, Vector.dot(a, b) / ((b.x * b.x + b.y * b.y + b.z * b.z) ** 2)); }
|
||||||
Vector.rejection = function rejection(a, b) { return Vector.subtract(a, Vector.projection(a, b)); }
|
Vector.rejection = function rejection(a, b) { return Vector.subtract(a, Vector.projection(a, b)); }
|
||||||
Vector.reflect = function reflect(v, n) { return Vector.subtract(v, Vector.multiply(n, 2 * Vector.dot(v, n))); }
|
Vector.reflect = function reflect(v, n) { return Vector.subtract(v, Vector.multiply(n, 2 * Vector.dot(v, n))); }
|
||||||
|
|||||||
Reference in New Issue
Block a user