/* * Image_Map Class */ class Dings_Image_Map_Class extends Dings_App_Class { /* Get Rectangle Map */ Get_Scaled_Polygon_List_From_Rectangle(Map_Area_Json) { let Scale_Ratio = this.Scale_Ratio let X = Map_Area_Json.Left_Top_X * Scale_Ratio; let Y = Map_Area_Json.Left_Top_Y * Scale_Ratio; let W = Map_Area_Json.Width * Scale_Ratio; let H = Map_Area_Json.Height * Scale_Ratio; let Polygon_List = [[X,Y, X+W,Y, X+W,Y+H, X, Y+H]] return Polygon_List } /* Get Polygon Map */ Get_Scaled_Polygon_List(Map_Area_Json) { let Scale_Ratio = this.Scale_Ratio var Polygon_List = [] // Iterate through Lists for (let List_Index = 0; List_Index < Map_Area_Json.Polygon_List.length; List_Index++) { let Point_List = Map_Area_Json.Polygon_List[List_Index] var Polygon = [] // Iterate through current List for (let Point_Index = 0; Point_Index < Point_List.length; Point_Index++) { let Scaled_Value = Math.round(Point_List[Point_Index] * Scale_Ratio) Polygon.push(Scaled_Value) } Polygon_List.push(Polygon) } return Polygon_List } /* Check if Point is inside the Polygon (Inspired by Chat-Gpt) */ Is_Point_In_Polygon(X, Y, Polygon) { var Num_Vertices = Polygon.length; var Inside = false; // The last Coordinats of the Polygon are used as Starting-Point for the Line var X_Prev = Polygon[Num_Vertices - 2]; var Y_Prev = Polygon[Num_Vertices - 1]; // Check each Vertice of the Polygon for (var i = 0; i < Num_Vertices; i += 2) { var X_Curr = Polygon[i]; var Y_Curr = Polygon[i + 1]; // Überprüfen Sie, ob die Linie die Kante des Polygons schneidet if ((Y_Curr > Y) !== (Y_Prev > Y) && (X < (X_Prev - X_Curr) * (Y - Y_Curr) / (Y_Prev - Y_Curr) + X_Curr)) { Inside = !Inside; } // Aktualisieren Sie die vorherigen Koordinaten X_Prev = X_Curr; Y_Prev = Y_Curr; } return Inside; } /* Check if Point is in Area */ Is_In_Map_Area(X, Y, Map_Area_Index) { if (Map_Area_Index == -1) return false var Polygon_List = this.Map_Areas[Map_Area_Index].Scaled_Polygon_List for (let Polygon_Index = 0; Polygon_Index < Polygon_List.length; Polygon_Index++) { if (this.Is_Point_In_Polygon(X, Y, Polygon_List[Polygon_Index])) { return true } } return false } /* Find Area_Index for Coordinates */ Find_Map_Area_Index(Touch_X, Touch_Y) { Touch_X -= this.Canvas_Offset_X Touch_Y -= this.Canvas_Offset_Y if (this.Is_In_Map_Area(Touch_X, Touch_Y, this.Map_Area_Index_Last)) return this.Map_Area_Index_Last for (let Map_Area_Index = 0; Map_Area_Index < Object.keys(this.Map_Areas).length; Map_Area_Index++) { if (this.Is_In_Map_Area(Touch_X, Touch_Y, Map_Area_Index)) return Map_Area_Index } return -1 } /* Open Dings-Page */ Open_Dings_Page(Map_Area_Index) { var Dings_Number = this.Map_Areas[Map_Area_Index].Dings_Number var Dings_Link = Dings_Number + ".html" window.open(Dings_Link, "_self") } /* Click-Event */ Handle_Click_Event(Map_Area_Index, Touch_X, Touch_Y) { if (Map_Area_Index < 0) return var Map_Area = this.Map_Areas[Map_Area_Index] var Touch_Time_Ms = Date.now() if (this.Map_Area_Index_Last == Map_Area_Index) { var Touch_Time_Diff_Ms = Touch_Time_Ms - this.Touch_Time_Last_Ms if (Touch_Time_Diff_Ms < 400 && Touch_Time_Diff_Ms > 50) { return this.Open_Dings_Page(Map_Area_Index) } } else { if (this.Map_Area_Index_Last >= 0) { if (this.Select_Mode.value == "Pre-Colored") { this.Map_Areas[this.Map_Area_Index_Last].Text_Visible = false } else if (this.Select_Mode.value == "Explore") { this.Map_Areas[this.Map_Area_Index_Last].Polygon_Visible = false this.Map_Areas[this.Map_Area_Index_Last].Text_Visible = false } } } if (this.Select_Mode.value != "Pre-Colored") Map_Area.Polygon_Visible = !Map_Area.Polygon_Visible if (this.Select_Mode.value == "Mark") Map_Area.Discovered = !Map_Area.Discovered Map_Area.Text_Visible = !Map_Area.Text_Visible Map_Area.Text_X = Touch_X - this.Canvas_Offset_X Map_Area.Text_Y = Touch_Y - this.Canvas_Offset_Y this.Draw_Canvas() this.Touch_Time_Last_Ms = Touch_Time_Ms this.Map_Area_Index_Last = Map_Area_Index } /* Get Mouse Coordinates and find Map-Area */ Handle_Mouse_Get_Map_Area_Index(Event) { Event.preventDefault(); const rect = Event.currentTarget.getBoundingClientRect(); const Touch_X = Event.clientX - rect.left; const Touch_Y = Event.clientY - rect.top; return [this.Find_Map_Area_Index(Touch_X, Touch_Y), Touch_X, Touch_Y] } /* Handle Mouse-Click Event */ Handle_Mouse_Click(Event) { if (this.Timeout_Id != undefined) clearTimeout(this.Timeout_Id) var Ret = this.Handle_Mouse_Get_Map_Area_Index(Event) var Map_Area_Index = Ret[0] var X = Ret[1] var Y = Ret[2] this.Finger_Width = 0 this.Handle_Click_Event(Map_Area_Index, X, Y) } /* Hanle Mouse-Move Event */ Handle_Mouse_Move(Event) { var Ret = this.Handle_Mouse_Get_Map_Area_Index(Event) var Map_Area_Index = Ret[0] var X = Ret[1] var Y = Ret[2] this.Finger_Width = 0 this.Map_Area_Focus_Index = Map_Area_Index if (this.Map_Area_Index_Last >= 0 && this.Map_Area_Index_Last != Map_Area_Index) { if (this.Select_Mode.value == "Pre-Colored") { this.Map_Areas[this.Map_Area_Index_Last].Text_Visible = false } else if (this.Select_Mode.value == "Explore") { this.Map_Areas[this.Map_Area_Index_Last].Text_Visible = false this.Map_Areas[this.Map_Area_Index_Last].Polygon_Visible = false } } this.Draw_Canvas(Map_Area_Index) if (this.Timeout_Id != undefined) clearTimeout(this.Timeout_Id) if (Map_Area_Index != -1 && this.Select_Mode.value != "Mark") { var Map_Area = this.Map_Areas[Map_Area_Index] this.Timeout_Id = setTimeout(function(){ Map_Area.Text_X = X - this.Canvas_Offset_X; Map_Area.Text_Y = Y - this.Canvas_Offset_Y; Map_Area.Text_Visible = true; this.Draw_Canvas(); }.bind(this), 300) Map_Area.Discovered = true } this.Map_Area_Index_Last = Map_Area_Index } /* Handle Touch-Device Events */ Handle_Touch(Event) { Event.preventDefault(); const Touch = Event.changedTouches[0]; const rect = Event.currentTarget.getBoundingClientRect(); // const Touch_X = Touch.screenX - rect.left; // const Touch_Y = Touch.screenY - rect.top; const Touch_X = Touch.clientX - rect.left; const Touch_Y = Touch.clientY - rect.top; this.Finger_Width = 25 var Map_Area_Index = this.Find_Map_Area_Index(Touch_X, Touch_Y) this.Handle_Click_Event(Map_Area_Index, Touch_X, Touch_Y) } /* Handle Select-Mode Change */ Handle_Select_Mode_Changed(Event) { if (this.Select_Mode.value == "Pre-Colored") { for (let [Map_Area_Index, Map_Area] of Object.entries(this.Map_Areas)) { Map_Area.Discovered = false Map_Area.Polygon_Visible = true Map_Area.Text_Visible = false } } else { for (let [Map_Area_Index, Map_Area] of Object.entries(this.Map_Areas)) { Map_Area.Discovered = false Map_Area.Polygon_Visible = false Map_Area.Text_Visible = false } } this.Map_Area_Index_Last = -1 this.Map_Area_Focus_Index = -1 this.Draw_Canvas() } /* Scale Image-Map for Window-Resize */ Update() { console.log("Image Map: Update()") let Image_Width_Actual = this.Image_Parent.clientWidth; let Image_Height_Actual = this.Image_Parent.clientHeight; // let Image_Width_Original = this.Image_Parent.naturalWidth; let Image_Width_Original = this.Dings_Json.Image.Width var Scaled_Polygon_List this.Canvas_Offset_X = 0 this.Canvas_Offset_Y = 0 let Width_Real, Height_Real if (this.Image_Parent.clientWidth / this.Image_Parent.clientHeight > this.Aspect_Ratio) { /* Space on Left / Right */ Width_Real = Math.round(Image_Height_Actual * this.Aspect_Ratio) Height_Real = this.Image_Parent.clientHeight this.Canvas_Offset_X = Math.round((Image_Width_Actual - Width_Real) / 2) } else { /* Space on Top / Bottom */ Width_Real = this.Image_Parent.clientWidth Height_Real = Math.round(Image_Width_Actual / this.Aspect_Ratio) this.Canvas_Offset_Y = Math.round((Image_Height_Actual - Height_Real) / 2) } const Scale_Ratio_New = Width_Real / Image_Width_Original; console.log("Scale_Ratio_New: " + Scale_Ratio_New) if (Scale_Ratio_New != this.Scale_Ratio) { const Scale_Ratio_Diff = Scale_Ratio_New / this.Scale_Ratio for (let Map_Area_Index = 0; Map_Area_Index < Object.keys(this.Map_Areas).length; Map_Area_Index++) { let Map_Area = this.Map_Areas[Map_Area_Index] Map_Area.Text_X = Map_Area.Text_X * Scale_Ratio_Diff Map_Area.Text_Y = Map_Area.Text_Y * Scale_Ratio_Diff } this.Scale_Ratio = Scale_Ratio_New } for (let Map_Area_Index = 0; Map_Area_Index < this.Dings_Json.Map_List.length; Map_Area_Index++) { let Map_Area_Json = this.Dings_Json.Map_List[Map_Area_Index]; let Map_Area = this.Map_Areas[Map_Area_Index]; if (Map_Area_Json.Type == "Rectangle") Scaled_Polygon_List = this.Get_Scaled_Polygon_List_From_Rectangle(Map_Area_Json) else if (Map_Area_Json.Type == "Polygon") Scaled_Polygon_List = this.Get_Scaled_Polygon_List(Map_Area_Json) else throw new Error("Invalid Map-Type: " + Map_Area.Type); Map_Area.Scaled_Polygon_List = Scaled_Polygon_List } this.Canvas.width = Width_Real this.Canvas.height = Height_Real this.Canvas.style.top = "" + (this.Image_Parent.offsetTop + this.Canvas_Offset_Y) + "px" this.Canvas.style.left = "" + (this.Image_Parent.offsetLeft + this.Canvas_Offset_X) + "px" this.Draw_Canvas() this.Map_Area_Index_Last = -1 } /* Build Object out of Json and Html_Id for the Widget */ constructor(Dings_Json, Html_Id) { super(Dings_Json, Html_Id) this.Html_Id = Html_Id; this.Dings_Json = Dings_Json; this.Image_Parent = document.getElementById(this.Html_Id + ".Image_Parent") this.Image_Child = document.getElementById(this.Html_Id + ".Image_Child") this.Select_Mode = document.getElementById(this.Html_Id + ".Select_Mode") this.Select_Mode.onchange = this.Handle_Select_Mode_Changed.bind(this) this.Image_Child.ontouchstart = this.Handle_Touch.bind(this) this.Image_Child.onmousemove = this.Handle_Mouse_Move.bind(this) this.Image_Child.onclick = this.Handle_Mouse_Click.bind(this) this.Aspect_Ratio = this.Dings_Json.Image.Width / this.Dings_Json.Image.Height this.Canvas = document.getElementById(this.Html_Id + ".Canvas"); this.Context = this.Canvas.getContext("2d"); this.Map_Area_Index_Last = -1 this.Line_Width = 3 this.Touch_Time_Last_Ms = Date.now() this.Map_Areas = {} this.Finger_Width = 0 /* Copy Data from Json */ for (let Map_Area_Index = 0; Map_Area_Index < this.Dings_Json.Map_List.length; Map_Area_Index++) { let Map_Area_Json = this.Dings_Json.Map_List[Map_Area_Index] var Map_Area = {} Map_Area.Text = Map_Area_Json.Text Map_Area.Color = Map_Area_Json.Color Map_Area.Dings_Number = Map_Area_Json.Dings_Number Map_Area.Discovered = false this.Map_Areas[Map_Area_Index] = Map_Area } /* Load State from Url */ let State_Dict = Dings_Lib.Dict_From_Url(Html_Id) console.log("Dict:") console.log(State_Dict) if ("Select_Mode" in State_Dict) this.Select_Mode.value = State_Dict["Select_Mode"] if ("Scale_Ratio" in State_Dict) this.Scale_Ratio = State_Dict["Scale_Ratio"] if ("Map_Area_Dict" in State_Dict) { for (let Map_Area_Index = 0; Map_Area_Index < Object.keys(this.Map_Areas).length; Map_Area_Index++) { let Map_Area = this.Map_Areas[Map_Area_Index] if (Map_Area_Index in State_Dict["Map_Area_Dict"]) { const Area_Dict = State_Dict["Map_Area_Dict"][Map_Area_Index] Map_Area.Text_Visible = Area_Dict["Text_Visible"] Map_Area.Text_X = Area_Dict["Text_X"] Map_Area.Text_Y = Area_Dict["Text_Y"] Map_Area.Polygon_Visible = Area_Dict["Polygon_Visible"] Map_Area.Discovered = Area_Dict["Discovered"] } } } // this.Update() } Get_State_Dict() { console.log("Get_State_Dict") let State_Dict = {} State_Dict["Select_Mode"] = this.Select_Mode.value State_Dict["Scale_Ratio"] = this.Scale_Ratio State_Dict["Map_Area_Dict"] = {} for (let Map_Area_Index = 0; Map_Area_Index < Object.keys(this.Map_Areas).length; Map_Area_Index++) { let Map_Area = this.Map_Areas[Map_Area_Index] if (!Map_Area.Polygon_Visible) continue State_Dict["Map_Area_Dict"][Map_Area_Index] = { "Text_Visible": Map_Area.Text_Visible, "Text_X": Map_Area.Text_X, "Text_Y": Map_Area.Text_Y, "Polygon_Visible": Map_Area.Polygon_Visible, "Discovered": Map_Area.Discovered } } return State_Dict } Draw_Text(Text, Start_X, Start_Y, Auto_Adjust=true) { let Image_Width = this.Image_Parent.clientWidth; let Image_Height = this.Image_Parent.clientHeight; let Context = this.Context var Max_Width = Image_Width / 2 * 0.7 // Text in Array von Wörtern aufteilen var Word_List = Text.split(' '); var Line = ''; var Line_List = []; var Line_Max_Width = 0 var Number_Of_Lines = 1 // const Font_Height_Bare = 20 * this.Scale_Ratio const Font_Height_Bare = this.Dings_Json.Image.Width / 40 * this.Scale_Ratio const Shadow_Pixel = 2 const Adjustment_Y = 3 const Margin = 3 const Min_Gap_Border = 2 // Text Context.font = Font_Height_Bare + "px" + " " + "Times New Roman" Context.textAlign = "left" // Text in Zeilen mit Umbruch aufteilen for (var i = 0; i < Word_List.length; i++) { var testLine = Line + Word_List[i] + ' '; var metrics = Context.measureText(testLine); var testWidth = metrics.width; if (testWidth > Max_Width && i > 0) { Line_Max_Width = Math.max(Line_Max_Width, Context.measureText(Line).width) Line_List.push(Line); Line = Word_List[i] + ' '; Number_Of_Lines += 1 } else { Line = testLine; } } var Metrics = Context.measureText(Line) Line_Max_Width = Math.max(Line_Max_Width, Metrics.width) let Font_Height_Full = Metrics.fontBoundingBoxAscent + Metrics.fontBoundingBoxDescent; let Line_Gap_All = (Font_Height_Full - Font_Height_Bare) * (Number_Of_Lines - 1) Line_List.push(Line); if (Auto_Adjust) { if (Start_X > Image_Width / 2) Start_X = Start_X - Line_Max_Width - this.Finger_Width else Start_X = Start_X + this.Finger_Width } // Calculate Box var Box_Left_Top_X = Start_X - Margin var Box_Left_Top_Y = Start_Y - Font_Height_Bare - Margin + Adjustment_Y var Box_Width = Line_Max_Width + 2 * Margin var Box_Height = Number_Of_Lines * Font_Height_Bare + Line_Gap_All + 2 * Margin // Adjust Y if we do not fit to Image if (Box_Left_Top_Y < 0) Box_Left_Top_Y = Min_Gap_Border if (Box_Left_Top_Y + Box_Height + Shadow_Pixel > Image_Height) Box_Left_Top_Y = Image_Height - (Box_Height + Shadow_Pixel) - Min_Gap_Border if (Box_Left_Top_X < 0) Box_Left_Top_X = Min_Gap_Border if (Box_Left_Top_X + Box_Width + Shadow_Pixel > Image_Width) Box_Left_Top_X = Image_Width - (Box_Width + Shadow_Pixel) - Min_Gap_Border // Shadow Context.fillStyle = 'rgba(130,130,130)' Context.fillRect(Box_Left_Top_X + Shadow_Pixel, Box_Left_Top_Y + Shadow_Pixel, Box_Width, Box_Height) // Box Context.fillStyle = 'rgba(255,255,255)' Context.fillRect(Box_Left_Top_X, Box_Left_Top_Y, Box_Width, Box_Height) // Zeichnen des Texts im Rechteck Context.fillStyle = "black" var Y = Box_Left_Top_Y + Font_Height_Bare var X = Box_Left_Top_X + Margin for (var i = 0; i < Line_List.length; i++) { if (Y + Font_Height_Full <= Box_Left_Top_Y + Image_Height) { Context.fillText(Line_List[i], X, Y); Y += Font_Height_Full; } else { break; } } } /* Draw Polygon for Area */ Draw_Area_Polygon(Map_Area_Index, Fill=true) { let Scale_Ratio = this.Scale_Ratio let Map_Area = this.Dings_Json.Map_List[Map_Area_Index]; let Context = this.Context var Polygon_List = this.Map_Areas[Map_Area_Index].Scaled_Polygon_List Context.lineWidth = this.Line_Width for (let Polygon_Index = 0; Polygon_Index < Polygon_List.length; Polygon_Index++) { Context.fillStyle = Map_Area.Color; Context.strokeStyle = Map_Area.Color; Context.beginPath(); const Point_List = Polygon_List[Polygon_Index] var First = true for (let List_Index = 0; List_Index < Point_List.length - 1; List_Index += 2) { const X = Point_List[List_Index + 0] const Y = Point_List[List_Index + 1] if (First) { Context.moveTo(X, Y) First = false } else { Context.lineTo(X, Y) } } Context.closePath(); if (Fill) Context.fill(); Context.stroke(); } } /* Draw Canvas */ Draw_Canvas() { let Width = this.Image_Parent.clientWidth; let Height = this.Image_Parent.clientHeight; let Discovered_Count = 0 let Map_Area_Count = Object.keys(this.Map_Areas).length console.log("Draw Canvas") this.Context.clearRect(0, 0, Width, Height) for (let Map_Area_Index = 0; Map_Area_Index < Map_Area_Count; Map_Area_Index++) { let Map_Area = this.Map_Areas[Map_Area_Index] if (Map_Area.Discovered) Discovered_Count += 1 if (Map_Area.Polygon_Visible) { this.Draw_Area_Polygon(Map_Area_Index, true) } else { this.Draw_Area_Polygon(Map_Area_Index, false) } } if (this.Map_Area_Focus_Index >= 0 && !this.Map_Areas[this.Map_Area_Focus_Index].Polygon_Visible) this.Draw_Area_Polygon(this.Map_Area_Focus_Index, true) // XXX: Enable, when we have Local-Data // this.Draw_Text(Discovered_Count + "/" + Map_Area_Count, Width, Height, false) for (let Map_Area_Index = 0; Map_Area_Index < Object.keys(this.Map_Areas).length; Map_Area_Index++) { let Map_Area = this.Map_Areas[Map_Area_Index] if (Map_Area.Text_Visible) { this.Draw_Text(Map_Area.Text, Map_Area.Text_X, Map_Area.Text_Y) } } } }