Jag började med Basic för att sen hoppa direkt över till Pascal och inline asm. Visst kan jag ibland sakna inline asm och jag gjorde ju för något år sedan en CPU som enbart tog maskinkod så jag har fått mitt av den varan också. Men jag kan inte påstå att jag vill skriva mer än innerloopar i asm.. skulle vara om man kodar på Amiga eller något där varje klockcykel räknas.
Modern C++ -programmering är bättre strukturerad och man kan göra mycket mer på samma tid. Moderna kompilatorer kickar en på asm också så det bara skriker om det. Jag har inte gjort något modernt test av att skriva innerloopar (tänk polygonfyllare eller linjeritare) men det vore lite kul att göra ett test av det igen…
Jag programmerar ju enbart för skojs skull och har gjort i 37 år. När jag jobbade med det tog det bort allt det roliga. Saker skule bara bli färdiga i tid kändes det som.
Känns bra att hittat tillbaka lite och jag är bättre än någonsin.
ChatGPT använder jag mest för komplexa matematiska funktioner, t.ex räkna ut catmull rom TCB splines:
void CurveBase::evaluate(const float pTime, float* pValue) {
const std::vector<Key>& keys = container().keys();
const size_t key_size = keys.size();
assert(key_size == mValue.size());
if (key_size == 0) {
memset(pValue, 0, sizeof(float) * mDimensions);
return;
}
// 1 nyckel eller före första -> returnera första
if (key_size == 1 || pTime <= keys.front().t) {
memcpy(pValue, mValue.front().value, sizeof(float) * mDimensions);
return;
}
// efter sista -> returnera sista
if (pTime >= keys.back().t) {
memcpy(pValue, mValue.back().value, sizeof(float) * mDimensions);
return;
}
// Hitta första index där t >= pTime, med bounds-skydd
uint32_t offset = 0;
while (offset < key_size && keys[offset].t < pTime) ++offset;
// här gäller: 1 <= offset <= key_size-1 (pga tidiga returer ovan)
// === Linear (oförändrad, men robust) ===
if (mType == Interpolation::Linear) {
const int i0 = int(offset) - 1;
const int i1 = int(offset);
const float t0 = keys[i0].t;
const float t1 = keys[i1].t;
const float td = t1 - t0;
const float percent = (td != 0.0f) ? ( (pTime - t0) / td ) : 0.0f;
for (size_t dim = 0; dim < mDimensions; ++dim) {
pValue[dim] = mValue[i0].value[dim]
+ (mValue[i1].value[dim] - mValue[i0].value[dim]) * percent;
}
return;
}
// === Smooth / CatmullRom — alignat med Linear: segment [offset-1, offset] ===
if (mType == Interpolation::Smooth || mType == Interpolation::CatmullRom) {
const int i0 = int(offset) - 1; // vänster nod
const int i1 = int(offset); // höger nod
const bool first = (i0 == 0);
const bool last = (i1 == int(key_size) - 1);
const float t0 = keys[i0].t;
const float t1 = keys[i1].t;
const float td = t1 - t0;
if (td == 0.0f) {
memcpy(pValue, mValue[i0].value, sizeof(float) * mDimensions);
return;
}
const float f = (pTime - t0) / td;
const float tPrev = first ? (t0 - 1.0f) : keys[i0 - 1].t;
const float tNext = last ? (t1 + 1.0f) : keys[i1 + 1].t;
const float denom1 = (t1 - tPrev);
const float denom2 = (tNext - t0);
const float tang1_t = (denom1 != 0.0f) ? (td / denom1) : 0.0f;
const float tang2_t = (denom2 != 0.0f) ? (td / denom2) : 0.0f;
for (size_t dim = 0; dim < mDimensions; ++dim) {
const float v1 = mValue[i0].value[dim];
const float v2 = mValue[i1].value[dim];
const float v0 = first ? v1 : mValue[i0 - 1].value[dim];
const float v3 = last ? v2 : mValue[i1 + 1].value[dim];
const float m1 = (v2 - v0) * tang1_t; // tangent vid v1
const float m2 = (v3 - v1) * tang2_t; // tangent vid v2
const float a = v1;
const float b = m1;
const float c = -(m2 - m1 - 3.0f * v2 + 3.0f * v1 + 3.0f * m1);
const float d = (v2 - v1 - m1 - c);
const float f2 = f * f;
pValue[dim] = a + b * f + c * f2 + d * f2 * f;
}
return;
}
// === B-Spline (uniform) — normalisera pTime till [0,1] och använd degree=3 ===
if (mType == Interpolation::BSpline) {
// degree p=3 => knots size = key_size + p + 1
const int p = 3;
const int n = int(key_size) - 1;
if (n < p) { // för få punkter, fallback: returnera närmaste
memcpy(pValue, mValue[offset - 1].value, sizeof(float) * mDimensions);
return;
}
std::vector<float> knots(key_size + p + 1);
// Uniform klampad knot-vektor [0..1]
// 0..0, linjärt, 1..1
for (int i = 0; i <= n + p + 1; ++i) {
if (i <= p) knots[i] = 0.0f;
else if (i >= n + 1) knots[i] = 1.0f;
else knots[i] = float(i - p) / float(n - p + 1);
}
// normalisera tid till [0,1]
const float tmin = keys.front().t;
const float tmax = keys.back().t;
const float u = (tmax == tmin) ? 0.0f : ( (pTime - tmin) / (tmax - tmin) );
// nollställ output
memset(pValue, 0, sizeof(float) * mDimensions);
// antag att CURVE_N(i, p, u, knots) finns och tar u i [0,1]
for (int i = 0; i <= n; ++i) {
const float Ni = CURVE_N(i, p, u, knots);
for (size_t dim = 0; dim < mDimensions; ++dim) {
pValue[dim] += mValue[i].value[dim] * Ni;
}
}
return;
}
// === TCB (Kochanek–Bartels) — segment [offset-1, offset] och bugfixar ===
if (mType == Interpolation::TCB) {
const int i0 = int(offset) - 1; // P1
const int i1 = int(offset); // P2
const bool first = (i0 == 0);
const bool last = (i1 == int(key_size) - 1);
const float t0 = keys[i0].t;
const float t1 = keys[i1].t;
const float td = t1 - t0;
if (td == 0.0f) {
memcpy(pValue, mValue[i0].value, sizeof(float) * mDimensions);
return;
}
const float f = (pTime - t0) / td;
for (size_t dim = 0; dim < mDimensions; ++dim) {
const float P0 = first ? mValue[i0].value[dim] : mValue[i0 - 1].value[dim];
const float P1 = mValue[i0].value[dim];
const float P2 = mValue[i1].value[dim];
const float P3 = last ? mValue[i1].value[dim] : mValue[i1 + 1].value[dim];
// Hämta T,C,B per nod (global eller per-key)
const bool useGlobal1 = mValue[i0].useGlobal;
const bool useGlobal2 = mValue[i1].useGlobal;
const float T1 = useGlobal1 ? keys[i0].T : mValue[i0].T;
const float C1 = useGlobal1 ? keys[i0].C : mValue[i0].C;
const float B1 = useGlobal1 ? keys[i0].B : mValue[i0].B;
const float T2 = useGlobal2 ? keys[i1].T : mValue[i1].T;
const float C2 = useGlobal2 ? keys[i1].C : mValue[i1].C;
const float B2 = useGlobal2 ? keys[i1].B : mValue[i1].B;
// Tangenter enligt Kochanek–Bartels
const float d10 = (P1 - P0);
const float d21 = (P2 - P1);
const float d32 = (P3 - P2);
const float m1 = (1 - T1) * ( (1 + C1) * (1 + B1) * d10 * 0.5f
+ (1 - C1) * (1 - B1) * d21 * 0.5f );
const float m2 = (1 - T2) * ( (1 - C2) * (1 + B2) * d21 * 0.5f
+ (1 + C2) * (1 - B2) * d32 * 0.5f );
// Hermite-koeff
const float a = P1;
const float b = m1;
const float c = -3.0f * P1 + 3.0f * P2 - 2.0f * m1 - m2;
const float d = 2.0f * P1 - 2.0f * P2 + m1 + m2;
const float f2 = f * f;
pValue[dim] = a + b * f + c * f2 + d * f2 * f;
}
return;
}
// fallback (ska aldrig nås om alla typer täcks)
memcpy(pValue, mValue.back().value, sizeof(float) * mDimensions);
}
… som resulterar i något sånt här:
(work in progress)



