{"record":{"id":"1593d3f5495a257c","repo":"GraphiteEditor/Graphite","slug":"failed-to-draw-hover-ring","errorCode":null,"errorMessage":"Failed to draw hover ring","messagePattern":"Failed to draw hover ring","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"editor/src/messages/portfolio/document/overlays/utility_types_web.rs","lineNumber":742,"sourceCode":"\t\tconst HOVER_RING_STROKE_WIDTH: f64 = HOVER_RING_OUTER_RADIUS - MAIN_RING_INNER_RADIUS;\n\t\tconst HOVER_RING_CENTERLINE_RADIUS: f64 = (HOVER_RING_OUTER_RADIUS + MAIN_RING_INNER_RADIUS) / 2.;\n\t\tconst MAIN_RING_STROKE_WIDTH: f64 = MAIN_RING_OUTER_RADIUS - MAIN_RING_INNER_RADIUS;\n\t\tconst MAIN_RING_CENTERLINE_RADIUS: f64 = (MAIN_RING_OUTER_RADIUS + MAIN_RING_INNER_RADIUS) / 2.;\n\n\t\tlet Some(show_hover_ring) = show_compass_with_hover_ring else { return };\n\n\t\tself.start_dpi_aware_transform();\n\n\t\tlet center = self.snap_to_physical_pixel_center(compass_center);\n\n\t\t// Save the old line width to restore it later\n\t\tlet old_line_width = self.render_context.line_width();\n\n\t\t// Hover ring\n\t\tif show_hover_ring {\n\t\t\tself.render_context.set_line_width(HOVER_RING_STROKE_WIDTH);\n\t\t\tself.render_context.begin_path();\n\t\t\tself.render_context.arc(center.x, center.y, HOVER_RING_CENTERLINE_RADIUS, 0., TAU).expect(\"Failed to draw hover ring\");\n\t\t\tself.render_context.set_stroke_style_str(COLOR_OVERLAY_BLUE_50);\n\t\t\tself.render_context.stroke();\n\t\t}\n\n\t\t// Arrows\n\t\tself.render_context.set_line_width(0.01);\n\t\tfor i in 0..4 {\n\t\t\tlet direction = DVec2::from_angle(i as f64 * FRAC_PI_2 + angle);\n\t\t\tlet color = if i % 2 == 0 { COLOR_OVERLAY_RED } else { COLOR_OVERLAY_GREEN };\n\n\t\t\tlet tip = center + direction * HOVER_RING_OUTER_RADIUS;\n\t\t\tlet base = center + direction * (MAIN_RING_INNER_RADIUS + MAIN_RING_OUTER_RADIUS) / 2.;\n\n\t\t\tlet r = (ARROW_RADIUS.powi(2) + MAIN_RING_INNER_RADIUS.powi(2)).sqrt();\n\t\t\tlet (cos, sin) = (MAIN_RING_INNER_RADIUS / r, ARROW_RADIUS / r);\n\t\t\tlet side1 = center + r * DVec2::new(cos * direction.x - sin * direction.y, sin * direction.x + direction.y * cos);\n\t\t\tlet side2 = center + r * DVec2::new(cos * direction.x + sin * direction.y, -sin * direction.x + direction.y * cos);\n","sourceCodeStart":724,"sourceCodeEnd":760,"githubUrl":"https://github.com/GraphiteEditor/Graphite/blob/c507b356453361e31638b8bff8f6d46b6da2961e/editor/src/messages/portfolio/document/overlays/utility_types_web.rs#L724-L760","documentation":"The compass/skew gizmo overlay draws its hover ring with ctx.arc(center.x, center.y, HOVER_RING_CENTERLINE_RADIUS, 0., TAU). The radius is a positive constant, so the negative-radius IndexSizeError route is closed; the .expect can only fire from non-finite center coordinates (compass_center run through snap_to_physical_pixel_center becoming NaN) on engines that reject non-finite doubles, or a regression making the constant non-positive.","triggerScenarios":"Rendering the transform compass hover ring when the computed compass center is NaN — typically from degenerate transform or viewport math upstream — or after a regression of the ring-radius constant.","commonSituations":"Gizmo overlays being the first consumer of corrupted geometry during pointer interaction; refactors turning gizmo constants into computed values.","solutions":["Validate the snapped center is finite before drawing the ring.","Trace NaN to its source in the compass center computation (document bounds, viewport transform).","Degrade .expect to a logged .ok() so the gizmo fails soft.","Keep gizmo radius constants positive via a const/debug assertion."],"exampleFix":"// before\nself.render_context.arc(center.x, center.y, HOVER_RING_CENTERLINE_RADIUS, 0., TAU).expect(\"Failed to draw hover ring\");\n\n// after\nif center.x.is_finite() && center.y.is_finite() {\n\tlet _ = self.render_context.arc(center.x, center.y, HOVER_RING_CENTERLINE_RADIUS, 0., TAU);\n}","handlingStrategy":"validation","validationCode":"fn compass_center_drawable(center: DVec2) -> bool {\n\tcenter.x.is_finite() && center.y.is_finite()\n}","typeGuard":"fn is_finite_point(p: DVec2) -> bool {\n\tp.x.is_finite() && p.y.is_finite()\n}","tryCatchPattern":"if let Err(err) = self\n\t.render_context\n\t.arc(center.x, center.y, HOVER_RING_CENTERLINE_RADIUS, 0., TAU)\n{\n\tlog::warn!(\"failed to draw hover ring: {:?}\", err);\n}","preventionTips":["Validate the snapped compass center once at the top of the gizmo draw","Const-assert gizmo radius constants stay positive","Log gizmo inputs on failure to catch upstream NaN quickly"],"tags":["wasm","canvas-2d","overlay","geometry","nan","panic"],"backgroundTag":"canvas-2d-negative-radius","analyzedSha":"c507b356453361e31638b8bff8f6d46b6da2961e","analyzedAt":"2026-08-16T21:57:18.596Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}