// engineering notes and technical solutions
Engineering Journal
Fixing Coordinate Drift & Centroid Shift on Vector PDF Blueprint Zoom
The Issue: While building the blueprint markup and inspection tool for Velder, we implemented a feature allowing engineers to drop pins on technical blueprints. However, when users pinched to zoom the PDF scheme, the coordinates drifted, causing the pins to slide away from their original positions on the canvas.
The Root Cause: Initially, the layout centered its zoom origin statically (top-left of canvas). On zoom, the coordinate space scaled, but the viewport scroll coordinate did not adjust proportionally around the user's pinch center, causing the background drawing to slide away.
The Solution:
- We tracked the user's two-finger coordinates to calculate the pinch focal point (the centroid between fingers) on touch start.
-
Applied a dynamic
transform-originbased on the focal point ratios (focal pixel offset relative to the canvas bounding rect width/height) during gesture. -
On gesture release, we temporarily stored the focal ratios (
focalRatioX,focalRatioY), triggered a canvas re-render via PDF.js with the new scale, and immediately adjusted the window scrolling viewport usingwindow.scrollBy.
Result: The drawing and pins remain perfectly locked under the user's fingers during zoom, keeping data entry 100% accurate.
// Storing focal ratios prior to resetting CSS transform scale
var cRect = canvas.getBoundingClientRect();
var focalRatioX = cRect.width > 0 ? (pinchFocalClientX - cRect.left) / cRect.width : 0.5;
var focalRatioY = cRect.height > 0 ? (pinchFocalClientY - cRect.top) / cRect.height : 0.5;
renderPage().then(function() {
wrap.style.transform = '';
wrap.style.transformOrigin = 'top left';
// Adjust scroll offset to keep the focal point stationary relative to viewport
requestAnimationFrame(function() {
var newRect = canvas.getBoundingClientRect();
var newFocalX = newRect.left + focalRatioX * newRect.width;
var newFocalY = newRect.top + focalRatioY * newRect.height;
window.scrollBy(newFocalX - pinchFocalClientX, newFocalY - pinchFocalClientY);
});
});
Speech-to-Text Voice Parser with Smart Polish Time & Action Extraction
The Issue: Velder technicians perform installation and maintenance of medical gas networks inside hospital zones (operating theatres, SOR) and plant/machine rooms. Often wearing protective masks and thick work gloves on site, typing reports on mobile screens is highly impractical. Additionally, cell signals in thick concrete basements are non-existent, making manual logging difficult.
The Constraint: Hospital basements are concrete bunkers with poor cellular network. Relying on heavy cloud NLP processing (like Dialogflow or OpenAI APIs) was impossible due to constant connection timeouts.
The Solution: We built a lightweight, client-side Polish Natural Language Processing (NLP) regex-based parser. Once the on-device engine transcribes speech to raw text, our parser:
- Detects relative date indicators (e.g., "jutro" [tomorrow], "dzisiaj" [today], names of weekdays, ordinal numerals like "pierwszego" [first]).
- Extracts time modifiers (e.g., "rano" [morning], "wieczorem" [evening], "o 14" [at 2 PM]).
- Strips out Polish grammatical conjunctions and function words to isolate a clean action title, auto-creating a task with the parsed timestamp.
Result: Workers can say "przypomnij mi jutro o 10 o teście szczelności" (remind me tomorrow at 10 AM about the pipeline tightness test), and the app logs a clean task: "teście szczelności" set for tomorrow at 10:00, 100% offline.
// Abstract representation of date parsing from Polish speech
export function parsePolishDate(input: string): Date {
const now = new Date();
let target = new Date(now);
if (/jutro/i.test(input)) {
target.setDate(now.getDate() + 1);
}
const timeMatch = input.match(/o\s+(\d{1,2})/i);
if (timeMatch) {
target.setHours(parseInt(timeMatch[1], 10), 0, 0, 0);
}
return target;
}
Background Sync & Conflict-Free SQLite Write Queue for Offline Estimating
The Issue: HVAC installation sites are often in rural basements or remote concrete bunkers. Technicians need to build estimates containing hundreds of pipes, manifolds, and services. If the app loses connection, actions failed, causing lost work.
The Solution: We implemented an offline-first transaction queue using Expo SQLite. When a technician adds materials offline:
-
Changes are written locally and serialized into an
offline_mutationstable as delta mutations. - NetInfo monitors network status in the background.
- On reconnection, a queue-manager drains mutations sequentially to Firebase Cloud Functions.
Result: 100% data integrity with conflict resolution (applying "last-write-wins" on dirty fields).
// Draining SQLite offline mutation queue sequentially
async function syncOfflineQueue() {
const mutations = await db.getAllAsync('SELECT * FROM offline_mutations ORDER BY timestamp ASC');
for (const mut of mutations) {
try {
await api.post('/sync-mutation', JSON.parse(mut.payload));
await db.runAsync('DELETE FROM offline_mutations WHERE id = ?', mut.id);
} catch (err) {
if (err.status === 409) {
await resolveConflict(mut);
} else {
break; // retry on next network change
}
}
}
}
Compiling Multi-Page branded PDF Estimates on Device in under 1.5s
The Issue: Generating large branded HVAC PDF estimates directly on-device was causing UI lag and memory crashes on low-end Android phones because of heavy font parsing and large graphic assets.
The Solution:
We refactored the PDF compile pipeline using
react-native-html-to-pdf
in an isolated thread. We converted high-resolution logos to optimized WebP base64
strings, embedded lightweight Google Fonts directly in the stylesheet, and built the PDF
page-by-page asynchronously.
Result: Memory overhead reduced by 70%, and generation time dropped from 6s to 1.2s, operating entirely client-side.
// Async PDF compilation chunks on background thread
const htmlContent = `
\${renderEstimateTable(estimateData)}
`;
const file = await RNHTMLtoPDF.convert({
html: htmlContent,
fileName: \`estimate_\${estimateId}\`,
directory: 'Documents',
});