@numericelements/knot-sequence
Version:
A library for generating and manipulating knot sequences for b-spline curves and surfaces
217 lines (214 loc) • 10.1 kB
JavaScript
import { WarningLog } from './errorProcessing/ErrorLoging.js';
import { KNOT_COINCIDENCE_TOLERANCE, UPPER_BOUND_NORMALIZED_BASIS_DEFAULT_ABSCISSA, KNOT_SEQUENCE_ORIGIN } from './namedConstants/KnotSequences.js';
import { AbstractPeriodicKnotSequence } from './AbstractPeriodicKnotSequence.js';
import { Knot } from './Knot.js';
import { INCREASINGPERIODICKNOTSEQUENCE } from './KnotSequenceConstructorInterface.js';
import { fromIncreasingPeriodicToStrictlyIncreasingPeriodicKnotSequence } from './KnotSequenceAndUtilities/fromIncreasingPeriodicToStrictlyIncreasingPeriodicKnotSequence.js';
import { EM_KNOTINDEX_INC_SEQ_TOO_LARGE, EM_KNOTSEQ_MULTIPLICITIES_INCOMPATIBLE_NORMALIZEDBASIS, EM_START_INDEX_OUTOF_RANGE, EM_START_INDEX_GREATER_THAN_END_INDEX, EM_INDICES_SPAN_TWICE_PERIOD, EM_U_OUTOF_KNOTSEQ_RANGE } from './ErrorMessages/KnotSequences.js';
import { KnotIndexStrictlyIncreasingSequence } from './KnotIndexStrictlyIncreasingSequence.js';
import { KnotIndexIncreasingSequence } from './KnotIndexIncreasingSequence.js';
import { WM_ABSCISSA_NOT_FOUND_IN_SEQUENCE } from './WarningMessages/KnotSequences.js';
class IncreasingPeriodicKnotSequenceClosedCurve extends AbstractPeriodicKnotSequence {
constructor(maxMultiplicityOrder, knotParameters) {
super(maxMultiplicityOrder, knotParameters);
this._indexKnotOrigin = new KnotIndexStrictlyIncreasingSequence(0);
if (knotParameters.type === INCREASINGPERIODICKNOTSEQUENCE) {
this.generateKnotSequence(knotParameters);
this.checkKnotMultiplicitiesAtNormalizedBasisBoundaries();
}
this.checkUniformityOfKnotMultiplicity();
this.checkUniformityOfKnotSpacing();
this.checkNonUniformKnotMultiplicityOrder();
this.checkNormalizedBasisOrigin();
}
get allAbscissae() {
const abscissae = [];
for (const knot of this) {
if (knot !== undefined)
abscissae.push(knot);
}
return abscissae;
}
[Symbol.iterator]() {
let knotAmount = 0;
const knotIndicesKnotAbscissaChange = [];
for (const multiplicity of this.multiplicities()) {
knotAmount = knotAmount + multiplicity;
knotIndicesKnotAbscissaChange.push(knotAmount);
}
const lastIndex = knotAmount - 1;
let indexAbscissaChange = 0;
let index = 0;
return {
next: () => {
if (index <= lastIndex) {
if (index === knotIndicesKnotAbscissaChange[indexAbscissaChange]) {
indexAbscissaChange++;
}
index++;
return { value: this.knotSequence[indexAbscissaChange].abscissa, done: false };
}
else {
index = 0;
return { done: true };
}
}
};
}
clone() {
return new IncreasingPeriodicKnotSequenceClosedCurve(this._maxMultiplicityOrder, { type: INCREASINGPERIODICKNOTSEQUENCE, periodicKnots: this.allAbscissae });
}
length() {
let length = 0;
for (const knot of this) {
if (knot !== undefined)
length++;
}
return length;
}
knotIndexInputParamAssessment(index, methodName) {
if (index.knotIndex > (this.allAbscissae.length - 1)) {
this.throwRangeErrorMessage(methodName, EM_KNOTINDEX_INC_SEQ_TOO_LARGE);
}
}
generateKnotSequence(knotParameters) {
const minValueMaxMultiplicityOrder = 1;
this.constructorInputMultOrderAssessment(minValueMaxMultiplicityOrder);
this.constructorInputArrayAssessment(knotParameters);
this.checkKnotIncreasingValues(knotParameters.periodicKnots);
this.knotSequence.push(new Knot(knotParameters.periodicKnots[0], 1));
for (let i = 1; i < knotParameters.periodicKnots.length; i++) {
if (knotParameters.periodicKnots[i] === this.knotSequence[this.knotSequence.length - 1].abscissa) {
this.knotSequence[this.knotSequence.length - 1].multiplicity++;
}
else {
this.knotSequence.push(new Knot(knotParameters.periodicKnots[i], 1));
}
}
this.checkMaxMultiplicityOrderConsistency();
const cumulative_multiplicities = knotParameters.periodicKnots.length - this.knotSequence[this.knotSequence.length - 1].multiplicity;
if ((cumulative_multiplicities < this._maxMultiplicityOrder && this._maxMultiplicityOrder > 1) ||
(cumulative_multiplicities < (this._maxMultiplicityOrder + 1) && this._maxMultiplicityOrder === 1)) {
this.throwRangeErrorMessage("generateKnotSequence", EM_KNOTSEQ_MULTIPLICITIES_INCOMPATIBLE_NORMALIZEDBASIS);
}
this._uMax = this.knotSequence[this.knotSequence.length - 1].abscissa;
this.checkNormalizedBasisOrigin();
this.checkKnotMultiplicitiesAtNormalizedBasisBoundaries();
}
raiseKnotMultiplicity(index, multiplicity) {
const newKnotSequence = this.clone();
newKnotSequence.raiseKnotMultiplicityArrayMutSeq(index, multiplicity);
return newKnotSequence;
}
insertKnot(abscissae, multiplicity = 1) {
const newKnotSequence = this.clone();
newKnotSequence.insertKnotMutSeq(abscissae, multiplicity);
return newKnotSequence;
}
knotMultiplicityAtAbscissa(abcissa) {
let multiplicity = 0;
for (const knot of this.knotSequence) {
if (Math.abs(abcissa - knot.abscissa) < KNOT_COINCIDENCE_TOLERANCE) {
multiplicity = knot.multiplicity;
}
}
if (multiplicity === 0) {
const warning = new WarningLog(this.constructor.name, "knotMultiplicityAtAbscissa", WM_ABSCISSA_NOT_FOUND_IN_SEQUENCE);
warning.logMessage();
}
return multiplicity;
}
abscissaAtIndex(index) {
let abscissa = UPPER_BOUND_NORMALIZED_BASIS_DEFAULT_ABSCISSA;
const multLastKnot = this.knotSequence[this.knotSequence.length - 1].multiplicity;
const indexPeriod = new KnotIndexIncreasingSequence(index.knotIndex % (this.allAbscissae.length - multLastKnot));
let i = 0;
for (const knot of this) {
if (i === indexPeriod.knotIndex && knot !== undefined)
abscissa = knot;
i++;
}
return abscissa;
}
toKnotIndexStrictlyIncreasingSequence(index) {
const strictlyIncreasingKnotSequence = fromIncreasingPeriodicToStrictlyIncreasingPeriodicKnotSequence(this);
const lastIdxStrictIncSeq = strictlyIncreasingKnotSequence.allAbscissae.length - 1;
const abscissa = this.abscissaAtIndex(index);
let i = 0;
for (const knot of strictlyIncreasingKnotSequence.allAbscissae) {
if (knot !== undefined) {
if (knot === abscissa)
break;
i++;
}
}
if (index.knotIndex > (this.allAbscissae.length - 1))
i = i + lastIdxStrictIncSeq;
return new KnotIndexStrictlyIncreasingSequence(i);
}
extractSubsetOfAbscissae(knotStart, knotEnd) {
let knots = [];
const sequence = this.allAbscissae.slice();
const lasIndex = this.allAbscissae.length - 1;
const multFirstKnot = this.knotSequence[0].multiplicity;
if (knotEnd.knotIndex <= knotStart.knotIndex) {
this.throwRangeErrorMessage("extractSubsetOfAbscissae", EM_START_INDEX_OUTOF_RANGE);
}
if (knotStart.knotIndex > lasIndex) {
this.throwRangeErrorMessage("extractSubsetOfAbscissae", EM_START_INDEX_GREATER_THAN_END_INDEX);
}
if ((knotEnd.knotIndex - knotStart.knotIndex) > (2 * lasIndex)) {
this.throwRangeErrorMessage("extractSubsetOfAbscissae", EM_INDICES_SPAN_TWICE_PERIOD);
}
if (knotEnd.knotIndex > lasIndex) {
for (let i = multFirstKnot; i < this.allAbscissae.length; i++) {
sequence.push(this.allAbscissae[i] + this.allAbscissae[lasIndex]);
}
}
if (knotEnd.knotIndex >= (2 * lasIndex)) {
for (let i = multFirstKnot; i < this.allAbscissae.length; i++) {
sequence.push(this.allAbscissae[i] + 2 * this.allAbscissae[lasIndex]);
}
}
knots = sequence.slice(knotStart.knotIndex, knotEnd.knotIndex + 1);
return knots;
}
findSpan(u) {
let index = UPPER_BOUND_NORMALIZED_BASIS_DEFAULT_ABSCISSA;
if (u > this.knotSequence[this.knotSequence.length - 1].abscissa) {
u = u % this.getPeriod();
}
if (u < KNOT_SEQUENCE_ORIGIN) {
this.throwRangeErrorMessage("findSpan", EM_U_OUTOF_KNOTSEQ_RANGE);
}
else {
if (this.isAbscissaCoincidingWithKnot(u)) {
index = 0;
for (const knot of this.knotSequence) {
index += knot.multiplicity;
if (Math.abs(u - knot.abscissa) < KNOT_COINCIDENCE_TOLERANCE) {
if (knot.abscissa === this.knotSequence[this.knotSequence.length - 1].abscissa) {
index -= this.knotSequence[this.knotSequence.length - 1].multiplicity;
}
index -= 1;
break;
}
}
return new KnotIndexIncreasingSequence(index);
}
index = this.findSpanWithAbscissaDistinctFromKnotIncreasingKnotSequence(u);
}
return new KnotIndexIncreasingSequence(index);
}
revertKnotSequence() {
const newKnotSequence = this.clone();
newKnotSequence.revertKnotSpacing();
return newKnotSequence;
}
decrementKnotMultiplicity(index) {
const newKnotSequence = this.clone();
newKnotSequence.decrementKnotMultiplicityMutSeq(index);
return newKnotSequence;
}
}
export { IncreasingPeriodicKnotSequenceClosedCurve };