import gleam/list import gleam/float import gleam/int pub type SketchStroke { SketchStroke( points: List(#(Float, Float)), color: String, thickness: Float, timestamp: String, ) } pub type GhostWritingSession { GhostWritingSession( session_id: String, reference_image: String, user_strokes: List(SketchStroke), ghost_image_opacity: Float, current_page: Int, total_pages: Int, ) } pub type SimilarityScore { SimilarityScore( overall_score: Float, structural_accuracy: Float, proportional_accuracy: Float, detail_completeness: Float, feedback: List(String), ) } pub fn start_ghost_writing_session( image_url: String, ) -> GhostWritingSession { GhostWritingSession( session_id: "SESSION_2026", reference_image: image_url, user_strokes: [], ghost_image_opacity: 0.3, current_page: 1, total_pages: 1, ) } pub fn create_stroke( points: List(#(Float, Float)), color: String, thickness: Float, ) -> SketchStroke { SketchStroke( points: points, color: color, thickness: thickness, timestamp: "2026-03-19T00:00:00Z", ) } pub fn add_stroke_to_session( session: GhostWritingSession, stroke: SketchStroke, ) -> GhostWritingSession { GhostWritingSession( ..session, user_strokes: [stroke, ..session.user_strokes], ) } pub fn adjust_ghost_opacity( session: GhostWritingSession, opacity: Float, ) -> GhostWritingSession { let clamped_opacity = case opacity { o if o <. 0.0 -> 0.0 o if o >. 1.0 -> 1.0 o -> o } GhostWritingSession(..session, ghost_image_opacity: clamped_opacity) } pub fn calculate_similarity( user_drawing: List(SketchStroke), _reference_image: String, ) -> SimilarityScore { let stroke_count = list.length(user_drawing) let structural_accuracy = case stroke_count { c if c > 15 -> 85.0 c if c > 8 -> 65.0 c if c > 3 -> 45.0 _ -> 20.0 } let proportional_accuracy = case stroke_count { c if c > 10 -> 80.0 _ -> 50.0 } let detail_completeness = case stroke_count { c if c > 15 -> 90.0 c if c > 8 -> 70.0 _ -> 40.0 } let overall = {structural_accuracy +. proportional_accuracy +. detail_completeness} /. 3.0 SimilarityScore( overall_score: overall, structural_accuracy: structural_accuracy, proportional_accuracy: proportional_accuracy, detail_completeness: detail_completeness, feedback: generate_drawing_feedback(overall, stroke_count), ) } fn generate_drawing_feedback(score: Float, stroke_count: Int) -> List(String) { let base_feedback = case score { s if s >. 85.0 -> [ "🏆 Exceptional job! Your drawing matches the reference perfectly!", "Your spatial understanding is excellent.", ] s if s >. 70.0 -> [ "💪 Great work! Your sketch is very accurate.", "Minor details could be refined.", ] s if s >. 50.0 -> [ "📈 Good effort! The main structure is recognizable.", "Try adding more detail.", ] _ -> [ "Keep practicing! Reference the ghost image more carefully.", "Add more strokes to complete the drawing.", ] } case stroke_count { c if c < 3 -> list.append(base_feedback, ["Remember: Draw more strokes for better results!"]) _ -> base_feedback } } pub fn generate_comparison_visualization( user_drawing: List(SketchStroke), _reference_image: String, ) -> String { let canvas = "" let user_svg = render_strokes(user_drawing) "