Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SW-3291 Quick text fonts are not loaded causing bounding box discrepancies #1805

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 0 additions & 7 deletions octoprint_mrbeam/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1165,13 +1165,6 @@ def wifi_wizard_api(self):
def lasersafety_wizard_api(self, data):
from flask.ext.login import current_user

# get JSON from request data, or send user back home
data = request.values
if hasattr(request, "json") and request.json:
data = request.json
else:
return make_response("Unable to interpret request", 400)

# check if username is ok
username = data.get("username", "")
if (
Expand Down
7 changes: 1 addition & 6 deletions octoprint_mrbeam/camera/lens.py
Original file line number Diff line number Diff line change
Expand Up @@ -607,17 +607,12 @@ def runLensCalibration(objPoints, imgPoints, imgRes, q_out=None):
# if callback: callback()
if q_out:
q_out.put(dict(ret=ret, mtx=mtx, dist=dist, rvecs=rvecs, tvecs=tvecs))
if ret == 0:
# TODO save to file here?

return ret, mtx, dist, rvecs, tvecs
else:
return ret, mtx, dist, rvecs, tvecs
return ret, mtx, dist, rvecs, tvecs


# NOTICE: This is used by the camera plugin
class CalibrationState(dict):

def __init__(
self,
imageSize=LEGACY_STILL_RES,
Expand Down
5 changes: 1 addition & 4 deletions octoprint_mrbeam/printing/comm_acc2.py
Original file line number Diff line number Diff line change
Expand Up @@ -2849,10 +2849,7 @@ def _set_compressor(self, value):

def _set_compressor_pause(self, paused):
try:
if paused:
_mrbeam_plugin_implementation.compressor_handler.set_compressor_pause()
else:
_mrbeam_plugin_implementation.compressor_handler.set_compressor_pause()
_mrbeam_plugin_implementation.compressor_handler.set_compressor_pause()
except:
self._logger.exception("Exception in _set_air_pressure() ")

Expand Down
209 changes: 168 additions & 41 deletions octoprint_mrbeam/static/js/app/helpers/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ $(function () {
/**
* https://stackoverflow.com/a/7616484
*/

// Even in slow 3g throttling mode, the font is loaded within 40ms
const QUICK_TEXT_FONT_LOAD_TIMEOUT = 50; // ms

String.prototype.hashCode = function () {
let hash = 0,
i,
Expand Down Expand Up @@ -32,50 +36,173 @@ $(function () {
});
};

url2png = async function (url, pxPerMM = 1, bbox = null, whiteBG = false) {
let prom = loadImagePromise(url).then(function (image) {
let x = 0;
let y = 0;
let w = image.naturalWidth; // or 'width' if you want a special/scaled size
let h = image.naturalHeight; // or 'height' if you want a special/scaled size
if (w === 0 || h === 0) {
const msg = `url2png: Image has no dimension!`;
console.error(msg, image);
throw new Error(msg);
}
if (bbox !== null) {
x = bbox.x;
y = bbox.y;
w = bbox.w;
h = bbox.h;
}
if (w === 0 || h === 0) {
const msg = `url2png: Source bbox has no dimension!`;
console.error(msg, image);
throw new Error(msg);
}
let canvas = document.createElement("canvas");
canvas.id = "RasterCanvas_url2png";
canvas.width = w * pxPerMM;
canvas.height = h * pxPerMM;
const ctx = canvas.getContext("2d");
if (whiteBG) {
ctx.fillStyle = "white";
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
generatePNGFromURL = async function (
url,
pxPerMM = 1,
bbox = null,
whiteBG = false,
includesQuickText = false
) {
// Load an image and wait for it to be loaded
const image = await loadImagePromise(url).catch((error) => {
console.error("Error caught in loadImagePromise:", error);
});

console.info(`c.drawImage ${x}, ${y}, ${w}, ${h}`);
// Get the image dimensions
let x = 0;
let y = 0;
let w = image.naturalWidth; // or 'width' if you want a special/scaled size
let h = image.naturalHeight; // or 'height' if you want a special/scaled size

ctx.drawImage(image, x, y, w, h, 0, 0, canvas.width, canvas.height);
const png = canvas.toDataURL("image/png");
const analysis = getCanvasAnalysis(canvas);
canvas.remove();
return { dataUrl: png, bbox: bbox, analysis: analysis };
// Check if image has dimension
_checkDimensionsAndThrowError(
w,
h,
`generatePNGFromURL: Image has no dimension!`
);

// Check if bbox has dimension
if (bbox !== null) {
x = bbox.x;
y = bbox.y;
w = bbox.w;
h = bbox.h;
}

// Check if bbox has dimension
_checkDimensionsAndThrowError(
w,
h,
`generatePNGFromURL: Bbox has no dimension!`
);

// Handle and draw image on canvas
let canvas = await _handleAndDrawImageOnCanvas(
whiteBG,
image,
x,
y,
w,
h,
pxPerMM,
includesQuickText
).catch((error) => {
console.error(
"Error caught in _handleAndDrawImageOnCanvas:",
error
);
});
// .catch(function (error) {
// console.error(`url2png: error loading image: ${error}`);
// });
return prom;

// Get PNG from canvas
const png = canvas.toDataURL("image/png");

// Get analysis from canvas
const analysis = getCanvasAnalysis(canvas);

// Remove canvas
canvas.remove();

return { dataUrl: png, bbox: bbox, analysis: analysis };
};

let _checkDimensionsAndThrowError = function (w, h, errorMessage) {
if (w === 0 || h === 0) {
console.error(errorMessage);
throw new Error(errorMessage);
}
};

let _handleAndDrawImageOnCanvas = async function (
whiteBG,
image,
x,
y,
w,
h,
pxPerMM,
includesQuickText
) {
// Create canvas
let canvas = document.createElement("canvas");
canvas.id = "RasterCanvas_generatePNGFromURL";
canvas.width = w * pxPerMM;
canvas.height = h * pxPerMM;
const ctx = canvas.getContext("2d");

// Draw image on canvas
let updatedCanvas = _checkWhiteBgAndDrawImage(
canvas,
ctx,
whiteBG,
image,
x,
y,
w,
h
);

// Check if quickText is included and wait for it to be redrawn
// The reason behind this is that quickText fonts might not be loaded yet
// So we wait a bit and redraw the image on the canvas to make sure the font is loaded
return new Promise((resolve) => {
if (includesQuickText) {
setTimeout(() => {
// Clear canvas
console.info(
`Clear canvas: x=${x}, y=${y}, canvas.width=${updatedCanvas.canvas.width}, canvas.height=${updatedCanvas.canvas.height}`
);
updatedCanvas.ctx.clearRect(
x,
y,
updatedCanvas.canvas.width,
updatedCanvas.canvas.height
);

// Redraw canvas after delay
let quickTextUpdatedCanvasAfterDelay =
_checkWhiteBgAndDrawImage(
updatedCanvas.canvas,
updatedCanvas.ctx,
whiteBG,
image,
x,
y,
w,
h
);

// resolve promise
resolve(quickTextUpdatedCanvasAfterDelay.canvas);
}, QUICK_TEXT_FONT_LOAD_TIMEOUT);
} else {
// resolve promise
resolve(updatedCanvas.canvas);
}
});
};

let _checkWhiteBgAndDrawImage = function (
canvas,
ctx,
whiteBG,
image,
x,
y,
w,
h
) {
// Draw white background if needed
if (whiteBG) {
ctx.fillStyle = "white";
ctx.fillRect(0, 0, canvas.width, canvas.height);
}

// Draw image on canvas
console.info(`Draw image: x=${x}, y=${y}, w=${w}, h=${h}`);
ctx.drawImage(image, x, y, w, h, 0, 0, canvas.width, canvas.height);

// Return canvas
return { canvas, ctx };
};

getCanvasAnalysis = function (canvas) {
Expand Down
89 changes: 54 additions & 35 deletions octoprint_mrbeam/static/js/app/snap-plugins/render-fills.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,11 @@ Snap.plugin(function (Snap, Element, Paper, global) {
lastOverlap = j;
}
}

// Update cluster if it includes QuickText elements
updateClusterIfItContainsQuickText(cluster);
}

clusters = clusters.filter((c) => c !== null);
if (lastOverlap === -1) {
// create new cluster
Expand Down Expand Up @@ -270,7 +274,7 @@ Snap.plugin(function (Snap, Element, Paper, global) {
return Promise.resolve(elem);
}

let prom = url2png(url).then((result) => {
let prom = generatePNGFromURL(url).then((result) => {
elem.attr("href", result.dataUrl);
return elem;
});
Expand Down Expand Up @@ -340,41 +344,44 @@ Snap.plugin(function (Snap, Element, Paper, global) {
h,
fontDeclarations
);
url2png(svgDataUrl, pxPerMM, bboxMM, true).then((result) => {
const size = getDataUriSize(result.dataUrl);

if (MRBEAM_DEBUG_RENDERING) {
console.info(
"MRBEAM_DEBUG_RENDERING",
result.dataUrl,
result.bbox
);
const img = elem.paper.image(
result.dataUrl,
result.bbox.x,
result.bbox.y,
result.bbox.w,
result.bbox.h
);
img.attr("opacity", 0.6);
img.click(function () {
img.remove();
});
const r = elem.paper.rect(result.bbox).attr({
fill: "none",
stroke: "#aa00aa",
strokeWidth: 2,
// includesQuickText is always true here as this method is only used on QuickText elements
generatePNGFromURL(svgDataUrl, pxPerMM, bboxMM, true, true).then(
(result) => {
const size = getDataUriSize(result.dataUrl);

if (MRBEAM_DEBUG_RENDERING) {
console.info(
"MRBEAM_DEBUG_RENDERING",
result.dataUrl,
result.bbox
);
const img = elem.paper.image(
result.dataUrl,
result.bbox.x,
result.bbox.y,
result.bbox.w,
result.bbox.h
);
img.attr("opacity", 0.6);
img.click(function () {
img.remove();
});
const r = elem.paper.rect(result.bbox).attr({
fill: "none",
stroke: "#aa00aa",
strokeWidth: 2,
});
setTimeout(() => r.remove(), 5000);
}

resolve({
dataUrl: result.dataUrl,
size: size,
bbox: bboxMM,
analysis: result.analysis,
});
setTimeout(() => r.remove(), 5000);
}

resolve({
dataUrl: result.dataUrl,
size: size,
bbox: bboxMM,
analysis: result.analysis,
});
});
);
});
return prom;
};
Expand Down Expand Up @@ -545,7 +552,7 @@ Snap.plugin(function (Snap, Element, Paper, global) {
// return promise;
// };

// TODO use url2png, simplify, check if necessary
// TODO use generatePNGFromURL, simplify, check if necessary
Element.prototype.renderJobTimeEstimationPNG = function (
wPT,
hPT,
Expand Down Expand Up @@ -723,4 +730,16 @@ Snap.plugin(function (Snap, Element, Paper, global) {
}
return [];
}

function updateClusterIfItContainsQuickText(cluster) {
cluster.elements.forEach((element) => {
let classListArray = Array.from(element.node.classList);
if (
classListArray.includes("straightText") ||
classListArray.includes("curvedText")
) {
cluster.includesQuickText = true;
}
});
}
});
Loading