{"record":{"id":"be09bcf6e618c831","repo":"GraphiteEditor/Graphite","slug":"transform-should-be-able-to-be-set-to-be-able-to-a","errorCode":null,"errorMessage":"transform should be able to be set to be able to account for DPI","messagePattern":"transform should be able to be set to be able to account for DPI","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"editor/src/messages/portfolio/document/overlays/utility_types_web.rs","lineNumber":613,"sourceCode":"\t}\n\n\tpub fn skew_handles(&mut self, edge_start: DVec2, edge_end: DVec2) {\n\t\tlet edge_dir = (edge_end - edge_start).normalize();\n\t\tlet mid = edge_end.midpoint(edge_start);\n\n\t\tfor edge in [edge_dir, -edge_dir] {\n\t\t\tself.draw_triangle(mid + edge * (3. + SKEW_TRIANGLE_OFFSET), edge, SKEW_TRIANGLE_SIZE, None, None);\n\t\t}\n\t}\n\n\t/// Transforms the canvas context to adjust for DPI scaling\n\t///\n\t/// Overwrites all existing tranforms. This operation can be reversed with [`Self::reset_transform`].\n\tfn start_dpi_aware_transform(&self) {\n\t\tlet [a, b, c, d, e, f] = DAffine2::from_scale(DVec2::splat(self.viewport.scale())).to_cols_array();\n\t\tself.render_context\n\t\t\t.set_transform(a, b, c, d, e, f)\n\t\t\t.expect(\"transform should be able to be set to be able to account for DPI\");\n\t}\n\n\t/// Un-transforms the Canvas context to adjust for DPI scaling\n\t///\n\t/// Warning: this function doesn't only reset the DPI scaling adjustment, it resets the entire transform.\n\tfn end_dpi_aware_transform(&self) {\n\t\tself.render_context.reset_transform().expect(\"transform should be able to be reset to be able to account for DPI\");\n\t}\n\n\tpub fn square(&mut self, position: DVec2, size: Option<f64>, color_fill: Option<&str>, color_stroke: Option<&str>) {\n\t\tlet size = size.unwrap_or(MANIPULATOR_GROUP_MARKER_SIZE);\n\t\tlet color_fill = color_fill.unwrap_or(COLOR_OVERLAY_WHITE);\n\t\tlet color_stroke = color_stroke.unwrap_or(COLOR_OVERLAY_BLUE);\n\n\t\tlet position = self.snap_to_physical_pixel_center(position);\n\t\tlet corner = position - DVec2::splat(size) / 2.;\n\n\t\tself.start_dpi_aware_transform();","sourceCodeStart":595,"sourceCodeEnd":631,"githubUrl":"https://github.com/GraphiteEditor/Graphite/blob/c507b356453361e31638b8bff8f6d46b6da2961e/editor/src/messages/portfolio/document/overlays/utility_types_web.rs#L595-L631","documentation":"start_dpi_aware_transform pushes a uniform scale matrix built from self.viewport.scale() via ctx.set_transform(...). The JS setTransform throws when any matrix entry is non-finite or the matrix is non-invertible. Because the matrix is a pure scale, the only realistic trigger is a non-finite viewport scale (NaN or Inf zoom) — and since every DPI-aware overlay primitive funnels through this helper, one bad scale panics on the first overlay draw of the frame.","triggerScenarios":"Viewport scale computed as NaN/Inf — e.g., dividing by a zero-size viewport during window resize or teardown, or a zoom animation hitting a division by zero — followed by any overlay draw calling start_dpi_aware_transform (square, manipulator, pivot, compass).","commonSituations":"Resizing the editor pane to 0x0 or during window teardown; zoom animation edge cases; hi-DPI scale computations dividing by zero dimensions.","solutions":["Guard the scale at its computation site: clamp zoom to a finite positive range and skip updates when viewport size is zero.","Skip overlay drawing entirely while the viewport has zero or non-finite dimensions.","Degrade the .expect to .ok() with a warn so one bad frame logs instead of crashing."],"exampleFix":"// before\nlet [a, b, c, d, e, f] = DAffine2::from_scale(DVec2::splat(self.viewport.scale())).to_cols_array();\nself.render_context\n\t.set_transform(a, b, c, d, e, f)\n\t.expect(\"transform should be able to be set to be able to account for DPI\");\n\n// after\nlet scale = self.viewport.scale();\nif !scale.is_finite() || scale <= 0. {\n\tlog::warn!(\"Non-finite viewport scale; skipping DPI-aware transform\");\n\treturn;\n}\nlet [a, b, c, d, e, f] = DAffine2::from_scale(DVec2::splat(scale)).to_cols_array();\nlet _ = self.render_context.set_transform(a, b, c, d, e, f);","handlingStrategy":"validation","validationCode":"fn viewport_scale_drawable(scale: f64) -> bool {\n\tscale.is_finite() && scale > 0.\n}\n// check before start_dpi_aware_transform runs","typeGuard":"fn is_finite_positive(scale: f64) -> bool {\n\tscale.is_finite() && scale > 0.\n}","tryCatchPattern":"if let Err(err) = self.render_context.set_transform(a, b, c, d, e, f) {\n\tlog::warn!(\"failed to set DPI transform: {:?}\", err);\n}","preventionTips":["Clamp zoom/scale to a finite positive range where it is computed","Skip overlay rendering while the viewport has zero dimensions (resize/teardown)","Never propagate non-finite scale into every overlay primitive via a shared helper"],"tags":["wasm","canvas-2d","overlay","dpi","nan","panic"],"backgroundTag":"canvas-2d-transform-non-invertible","analyzedSha":"c507b356453361e31638b8bff8f6d46b6da2961e","analyzedAt":"2026-08-16T21:57:18.596Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}