מנגנון הציור של Java

LironBar247

New member
מנגנון הציור של Java

שלום לכולם,
מדובר המשך של שאלה ששאלתי כאן בעבר, אך נתקלתי בעוד בעיה. אני רוצה שכאשר אני מגדילה את החלון (ה-frame), אז גם האליפסות יגדלו ויתאימו עצמן לגודל החדש של החלון. ניסיתי לעשות זאת בקטע הקוד שבהערה, אך זה לא פועל :( בעיה נוספת שיש לי ואיני מצליחה לפתור היא שכאשר אני מגדילה את החלון, מתבצע פיזור מחדש של צבעי האליפסות, למרות שהדבר אינו רצוי... תוכלו לנסות בבקשה לעזור לי? זה די דחוף...
אני מצרפת כאן את התרגילון שניסיתי לעשות ואת הקוד שכתבתי.
תודה לכל העוזרים!

 

BravoMan

Active member
התשובה לשתי השאלות שלך מאוד פשוטה:

תוסיפי את הקוד שעושה את מה שאת רוצה לעשות:
&nbsp
1. כרגע, את מציירת אליפסות בגודל קבוע שהגדרת בקוד.
ל-JPanel יש מתודות getWith ו-getHight מהן את יכולה לקבל את גודל החלון שלך, כך שבכל פעם ש-painComponent מופעל, את יכולה לחשב מחדש את גודל האליפסות הרצוי.
&nbsp
2. כרגע את מגרילה מחדש את הצבעים בכל פעם שאת מציירת מחדש.
אם את רוצה לשמור על הצבעים, תגרילי אותם פעם אחת, למשל אחרי שהמשתמש הכניס כמה אליפסות הוא רוצה, תשמרי אותם במערך כלשהו, ותשתמשי בו בפונקציית הציור במקום להגריל מחדש.
 

LironBar247

New member
ראשית, תודה!

אני חושבת שהצלחתי להתמודד עם 1 לאור מה שאמרת :)
לגבי 2 - אני עדיין מתקשה להבין איך לעשות זאת. הרי בכל מקרה גם בחירת הצבעים האקראית מתרחשת בתוך paintComponent, אז איך בעצם אני יודעת כן לבצע פיזור צבעים אם המשתמש הורה לי לעשות כן (ואז קוראים ל-repaint()) אבל להשאיר את הצבעים כמו שהם כאשר רק מגדילים את החלון? הרי בשני המקרים "עוברים דרך" paintComponent, לא?

ויש לי עוד שאלה:
כשאני מריצה עם קלט של מס' האליפסות n של עד כ-20 יש התאמה טובה בין גודל הפנל המוצג לפריים ע"פ מה שהוגדר לפריים במתודת setSize.
לעומת זאת עבור n גדולים יותר, לכיוון ה- 100, מתקבל פנל שלא מכסה את כל הפריים.
(הגדרתי פריים של 400x400 וקיבלתי כיסוי של בערך 385x365).
לא ברור לי מקור הבעיה.
בתחילה חשבתי שמקור הבעיה הוא שלא עשיתי המרה מקדימה ל- double בחישוב של נק' ה-ovalSize של כל אליפסה ולכן התמונה הכללית התכווצה
בגלל השארית בחילוק כ-int. אבל שללתי זאת מכיוון שהרי המתודה fillOval בכל מקרה מקבלת int ולא double, כך שבכל מקרה יאבד פה מידע, לא?

אני מצרפת כאן את הקוד שוב לאחר השינויים הקלים שעשיתי :)

שוב, תודה!!!
 

BravoMan

Active member
המתודה paintComponent אמורה לשמש

רק לציור עצמו, לא להחלטה מה צריך לצייר.
ככה זה בתרגיל הנוכחי, וככה זה לרוב בתוכנות אמתיות.
&nbsp
את צריכה לייצר ולשמור את ערכי הצבעים מחוץ למתודה הזו, ורק לקרוא אותם מהיכן ששמרת במתודה כשאת רוצה לצייר.
&nbsp
איפה בדיוק לשמור?
זה חלק מהתרגיל! אז אני לא יכול לפתור לך את זה.
אבל בתחלס - את צריכה להסתכל על מבנה התוכנית שלך, ולהחליט היכן הכי נוח לך לעשות זאת.
רמז: אפשר להוסיף איברים פרטיים ופונקציות ציבוריות כמה שרוצים ובאיזה צורה שרוצים למחלקה שיורשת מ-JPanel, זה לא יפריע לתפקוד הרגיל שלה.
 

LironBar247

New member
עדיין

אני לא מבינה משהו. הרי כתבו לנו בתרגיל להשתמש ב-repaint כדי לעדכן את הצבעים. והמתודה הזאת קוראת שוב למתודת הציור. אז מכאן אני מסיקה שחלק הקוד הנותן לאליפסות צבעים אקראיים כן צריך להופיע בתוך מתודת הציור, לא? מה אני לא מבינה טוב?

תוכל להתייחס גם לשאלתי השנייה בנוגע ליחס בין הפאנל לבין הפריים?
 

BravoMan

Active member
הסבר:

אמרו לך להשתמש ב-repaint, כי אסור לך לקרוא לפונקציה paintComponent ישירות.
paintComponent, מקבלת פרמטר Graphics. את הפרמטר הזה SWING מספק, את לא יכולה לספק אותו בעצמך.
repaint אומרת ל-SWING: "תקראי בבקשה ל-paintComponent, צריך לצבוע את הרכיב מחדש".
&nbsp
בנוסף, ל-JPanel שלך אין דרך לדעת שהמשתמש לך "כן" בתשובה לשאלה "האם לשנות פיזור צבעים".
&nbsp
אז למה החלטת שזה אומר שצריך להגריל צבעים בפונקציית הציור?
הרי זה אומר שבכל פעם שמציירים מחדש יהיו צבעים אחרים!
&nbsp
להפך, כתבו לך במפורש בתרגיל שצריך לשאול את המשתמש אם הוא רוצה פיזור צבעים חדש, וזה אומר שיש להגריל צבעים אך ורק כשהמשתמש אישר זאת.
&nbsp
ואת יודע שצריך לצייר מחדש גם כשהמשתמש לא אישר, אלא כשגודל החלון השתנה, או החלון עבר מאחורי חלון אחר וחזר, וכו'.
&nbsp
לגבי הסעיף השני - אני צריך לדבג את הקוד שלך כדי לדעת מה הבעיה.
סביר להניח שזו לא המרה מ-double ל-int, לא צריכה להיות בעיה כזו גדולה בערכים כאלה קטנים.
זה כנראה באג בקוד שלך, ואולי אמצא זמן מאוחר יותר לעבור עליו, אבל אני מציע לך בחום ללמוד לדבג דברים כאלה בעצמך.
זה יחסוך לך הרבה זמן וכאב ראש בעתיד.
&nbsp
אם לא לימדו אתכם עדיין להשתמש ב-debbuger, לפחות תכניסי System.out.println על כל הערכים שאת רוצה לדעת במקומות הנכונים בקוד, ותראי היכן משתבשים החישובים.
&nbsp
בהצלחה!
 

LironBar247

New member
תודה :)

הצלחתי הרגע לתת מענה לבעיה של פיזור הצבעים בהגדלת החלון. הבנתי סוף סוף למה התכוונת. מצטערת שלקח לי זמן. פשוט אני חדשה בנושא ולא מנוסה.
נשאר לי רק הסיפור של הפיקסלים.
ניסיתי לדבג ולהשתמש בהדפסות ובינתיים לא מצאתי את הנקודה הבעייתית.
אם יהיה לך זמן בהמשך, אשמח אם תסתכל על זה :)

ושוב אני שולחת את הקוד המעודכן פה.
 

BravoMan

Active member
טוב, שיחקתי עם הקוד שלך,

ואני לא רואה שום בעיה עם גודל הפאנל.
הוא תמיד תופס את כל הפריים.
קל לראות את זה אם קובעים לו צבע רק שונה מזה של הפריים, למשל כך:
setBackground(new Color(0, 255, 0));


כמובן, צריך לשים לב שהמסגרת של הפריים לוקחת קצת מהגודל, אבל אין באמת שום בעיה.

כמובן, תלוי בגודל הפריים, לא תמיד יתקבל מספר שלם של פיקסלים בחלוקה.
למשל, אצלי ב-Ubuntu כותרת החלון תופסת 28 פיקסלים מגובה הפריים.
זה אומר, שלפאנל נשאר גובה של 372 פיקסלים.

אם המשתמש מבקש לצייר 50 אליפסות, זה אומר 7.44 פיקסל לאליפסה.
כמובן, אי אפשר לצייר 0.44 פיקסל, ואם נעשה את הגובה 8 השורה האחרונה תחרוג מהחלון, אז במקום זה נותר רווח של 22 פיקסלים בתחתית החלון = (7 * 50) - 372

זה אולי לא יפה כ"כ, אבל תואם בצורה מלאה את תנאי התרגיל.

מה שכן, מצאתי שגיאה מאוד רצינית בקוד שלך: בתרגיל, כתוב לצייר אליפסות, אבל את מציירת עיגולים.
האם ברור לך מה ההבדל ומה ההשפעה שלו?
התיקון בקוד הוא מאוד פשוט, אבל כמובן זה חלק מהתרגיל, אז אשאיר לך לעשות אותו.

בנוסף, את עושה הרבה עבודה מיותרת כשאת נותנת שמות לצבעים ושומרת את השמות, במקום ישירות לשמור את הבצעים.
עלולים להוריד על זה ניקוד...
 

LironBar247

New member
האמת ש

רציתי באמת לשאול אותך על זה. אני משתמשת ב-fillOval כדי לצייר אליפסות, ובפועל מצוירים לי עיגולים, כמו שאמרת. האם זה בגלל שאני מזינה אורך ורוחב שווים למלבן החוסם את האליפסה? כלומר יוצרת "ריבוע חוסם"?
כלומר, כל מה שעליי לעשות זה להכפיל את ovalSize בשבר די קטן עבור הגובה ואת רוחב להשאיר כמובן ovalSize?

ושאלה עקרונית, שאני קצת מתביישת לשאול, אבל בכל זאת - עיגול זה לא מקרה פרטי של אליפסה?
 

BravoMan

Active member
קודם כל, לעולם אל תתביישי לשאול!

כן - עיגול הוא מקרה פרטי של אליפסה, בדיוק כמו שריבוע הוא מקרה פרטי של מלבן.
ונכון - את מקבלת עיגולים כי למעשה את נותנת ממדים שווים לגובה ורוחב של מלבן חוסם.
כלומר, בעצם יש לך ריבוע חוסם, כל הצדדים שווים, כל הרדיוסים שווים ויש עיגול.
&nbsp
עד כאן טוב ויפה, אבל בתרגיל כתבו במפורש "אליפסה", והזכירו גודל יחסי לגודל החלון.
&nbsp
מה שחשוב לשים לב אליו, זה שהמשתמש לא חייב כלל וכלל לשמור על החלון מרובע.
הוא יכול לשנות בנפרד את הגובה ואת הרוחב שלו.
&nbsp
שימי לב שהשאלה כאן אינה טכנית - לא מדובר בהבנה בתכנות, אלא במה בעצם מבקשים בתרגיל.
לכן, מעניין אותי לדעת - ברצינות, לא בצורה סרקסטית, למה חשבת שצריך להכפיל באיזשהו שבר דווקא?
מהתרגיל, אמור להיות די ברור שרוצים שתחשבי את הגובה ואת הרוחב בנפרד, לפי הגובה והרוחב של הפאנל.
אז, אם נבין באיזו דרך הלכת כדי להגיע לפתרון השגוי, אולי אפשר יהיה להמליץ על דרך נכונה לעתיד.
 

LironBar247

New member
עכשיו אני נורא מבולבלת

קראתי שוב ושוב את השאלה ותגיד לי אם זו כוונתך:
אתה הבנת שרוחב המלבן החוסם את האליפסה הוא בהתאם לרוחב הנוכחי של החלון וגובה האליפסה הוא בהתאם לגובה הנוכחי של החלון? כלומר רוחב לרוחב וגובה לגובה?
במילים אחרות, אם אני משנה את גודל החלון ל-100X185, והמשתמש הזין שהוא רוצה 10 אליפסות, אז מימדי המלבן החוסם כל אליפסה יהיו 10X18?
אם כן - אז האמת שאכן לא הבנתי זאת כך בהתחלה :(
עשיתי את השינוי הלוגי שלפי דעתי היה צריך לטפל בבעיה, ואני מצרפת פה שוב את הקוד (השינוי הוא התייחסות לגובה ולרוחב של הפריים ושינוי שטח האליפסה בהתאם). אבל שוב יש לי בעיה שאני לא מצליחה להבין את מקורה:
עכשיו שטחי האליפסות אכן משתנים באופן פרופורציוני לגודל החלון, אך הם גולשות ו"נמרחות" אחת לשטח השנייה.
עושה רושם כאילו "נקודות ההתחלה" שלהן נשארות במקום ולכן הן נמרחות, אבל נראה לכאורה בקוד שדאגתי לשנות את נקודות ההתחלה שלהן ע"י הכתיבה fillOval (i*ovalHeight, j*ovalWidth, ..., ... ). ניסיתי לדבג ולהשתמש בהדפסות, אבל איני מצליחה להבין מדוע זה קורה...
 

BravoMan

Active member
אכן, זו בדיוק כוונתי

וזו בדיוק הסיבה שביקשו בתרגיל אליפסות ולא עיגולים - רוחב לרוחב, גובה לגובה, והם לא חייבים להיות זהים.
&nbsp
באשר לדריסה - הבעיה שלך היא חוסר תשומת לב פשוט מצידך - את שמה פרמטרים של fillOval במקומות הלא נכונים!
תסתכלי טוב טוב איזה פרמטר הוא גובה (ציר ה-Y), ואיזה רוחב (ציר ה-X).
רמז: כל פונקציה מסודרת תשים אותם תמיד באותו סדר, אם יש יותר מזוג אחד שלהם.
 

LironBar247

New member
צודק לגמרי

עובד מעולה!!

מודה לך על כל התשובות ועל העזרה!

באמת שמעריכה את זה!
 
למעלה