Ներածություն
Իմ նպատակն է ցուցադրել, թե ինչես կարելի է Java լեզվով գրված ծրագրի ֆունկցիոնալությունն ընդլայնել սկրիպտային մեխանիզմի միջոցով՝ օգտագործողին տալով գործողությունների մեծ ազատություն: Որպես մոդել դիտարկում եմ JPanel-ի վրա հատվածների միջոցով տարբեր երկրաչափական պատկերների ստացման խնդիրը:
Եթե չօգտագործվեր սկրիպտային մեխանիզմը, ապա կամայական պատկեր ստանալու համար կան հետևյալ եղանակները.
- Նախատեսել ստանդարտ գործողություններ, որոնք օգտագործողին մատչելի կլինեն որպես մենյուի կամ գործիքների տողի գործողություններ: Այս դեպքում օգտագործողը ստիպված կլինի մկնիկով բազմաթիվ գործողություններ կատարել, իսկ կառուցված պատկերի համար մեծ ճշտություն ստանալն էլ դժվար կլինի:
- Նախագծել մի նոր, հատկապես տվյալ խնդիրը սպասարկելու համար նախատեսված լեզու, որն իր հրամանների համակարգում կունենա գրաֆիկական պրիմիտիվներ (տվյալ դեպքում՝ հատված) պատկերելու հրամաններ: Այս դեպքում էլ ստիպվալ կլինենք գրաֆիկական պրիմիտիվներից բացի իրականացնել ալգորիթմներ կազմելու համար անգրաժեշտ հրահանգները՝ պայման, ցիկլ, ֆւնկցիաներ կամ պրոցեդուրաներ և այլն:
Իմ կարծիքով, նպատակահարմար է Java-ի գրաֆիկական միջոցներով (այս դեպքում՝ Swing) իրականացնել տրված սկզբնակետի ու վերջնակետի կոորդինատներ ունեցող հատված պատկերման կոդը (ես դա արել եմ JPanel-ի ընդլայնմամբ), ապա սկրիպտային լեզվով (այս դեպքում՝ Armed Bear Common Lisp) իրականացնել մյուս բոլոր անհրաժեշտ գրաֆիկական հրամանները:
Հատվածների պատկերում
Հատվածները պատկերելու համար ընդլայնվել է JPanel դասը: Եթե խոսենք ավելի հասկանալի տերմիններով, ապա paintComponent մեթոդում իրականացված է գրաֆիկական հրամանների վիրտուալ մեքենա, որը կատարում է երկու հրաման՝ հատվածի պատկերում և գույնի ընտրություն:
package jrobot; import java.awt.Color; import java.awt.Graphics; import javax.swing.JPanel; public class RobotField extends JPanel { /**/ private CommandList commands = null; /**/ public RobotField() { setBackground(Color.white); } /**/ public void draw(CommandList com) { commands = com; repaint(); } /**/ @Override protected void paintComponent(Graphics g) { super.paintComponent(g); if( null == commands ) return; int index = 0; while( index < commands.size() ) { if( commands.get(index) == CommandList.O_LINE ) { g.drawLine(commands.get(index+1), commands.get(index+2), commands.get(index+3), commands.get(index+4)); index += 5; } else if( commands.get(index) == CommandList.O_COLOR ) { g.setColor(new Color(commands.get(index+1), commands.get(index+2), commands.get(index+3))); index += 4; } } } }
paintComponent մեթոդը ենթադրում է, որ commands ցուցակում հանդիպող թվերից առաջինը գործողությանա կոդն է, իսկ նրան հաջորդողներն արգումենտներն են՝ համապատասխան քանակով: Այս իրականացման մեջ հատվածի (O_LINE) գործողությունն օգտագործում է իրեն հաջորդող չորս թվերը, իսկ գույնի (O_COLOR) ընտրման գործողությունը՝ իրեն հաջորդող երեք թվերը:
commands-ը CommandList դասին պատկանող օբյեկտ է: Այդ դասը ArrayList<Integer> ցուցակի ընդլայնումն է, որտեղ ավելացրած են երկու մեթոդներ՝ addLine և addColor, պարզապես աշխատանքի հարմարության համար:
package jrobot; import java.util.ArrayList; public class CommandList extends ArrayList<Integer> { public static final int O_LINE = 0x01; public static final int O_COLOR = 0x02; /**/ public void addLine(int x0, int y0, int x1, int y1) { add(Integer.valueOf(O_LINE)); add(Integer.valueOf(x0)); add(Integer.valueOf(y0)); add(Integer.valueOf(x1)); add(Integer.valueOf(y1)); } /**/ public void addColor(int r, int g, int b) { add(Integer.valueOf(O_COLOR)); add(Integer.valueOf(r)); add(Integer.valueOf(g)); add(Integer.valueOf(b)); } }
Ահա այսքանը բավարար է հատվածների պատկերումն ապահովելու համար:
JSR-223 մեխանիզմի օգտագործումը
JSR-223 մեխանիզմը հնարավորություն է տալիս Java ծրագրերում օգտագործել ստրիպտային լեզուների ամբողջ հզորությունը: Բնականաբար այստեղ ես չեմ ներկայացնի այդ մեխանիզմի բոլոր հնարավորությունները, այլ կպատմեմ, թե ինչպես է օգտագործվել Armed Bear Common Lisp (ABCL) լեզուն գրաֆիկական հրամաններ ստեղծելու համար:
ABCL-ի ինտերպրետատորը ծրագրում օգտագործելու համար MainWindow դասում նախատեսված է հետևյալը.
private ScriptEngine engine = null;
engine օբյեկտը ստեղծվում է createInterpreter մեթոդով, որը նախ ստեղծում է Lisp-ի ինտերպրետատորը, ապա բեռնում է համակարգի ինիցալիզացիայի drawersetup.lisp ֆայլը (այդ ֆայլի մասին՝ ստորև).
private void createInterpreter() { ScriptEngineManager factory = new ScriptEngineManager(); engine = factory.getEngineByName("ABCL"); try { engine.eval(new FileReader("drawersetup.lisp")); } catch(Exception ex) { System.err.println(ex.getMessage()); } }
ScriptEngineManager դասի getEngineByName մեթոդը ստեղծում է տրված անունով լեզվի ինտերպրետատոր այն դեպքում, երբ համակարգին հասանելի է համապատասխան լեզվի իրականացման jar ֆայլը. տվյալ դեպքում դա abcl.jar ֆայլն է:
- ստեղծում է նոր CommandList օբյեկտ,
- այդ օբյեկտը հասանելի է դառնում Lisp-ի ինտերպրետատորին *commands* անունով,
- ինտերպրետատորին կատարման է տալիս սկրիպտի դաշտի պարունակությունը,
- JPanel-ի նմուշին է փոխանցվում արժեքներով լցված ցուցակը:
private void runLispScript()
{ CommandList test = new CommandList(); engine.put("*commands*", test); try { engine.eval(scriptCode.getText()); } catch(Exception ex) { JOptionPane.showMessageDialog(this, ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE); } robotField.draw(test); }
Գրաֆիկական հրամանները Common Lisp լեզվով
(defstruct point x y)
(defconstant +start-point+ (make-point :x 0 :y 0))
(defun point-translate-cart (p dx dy) (make-point :x (+ dx (point-x p)) :y (+ dy (point-y p))))
(defun point-translate-polar (pos angle dist) (let* ((rad (/ (* pi angle) 180)) (dx (truncate (* dist (cos rad)))) (dy (truncate (* dist (sin rad))))) (point-translate-cart pos dx (* -1 dy))))
(defstruct color red green blue) (defconstant +black+ (make-color :red 0 :green 0 :blue 0)) (defconstant +white+ (make-color :red 255 :green 255 :blue 255))
(defstruct segment begin end)
(defun segment-from-turn-go (pos ang dis) (make-segment :begin pos :end (point-translate-polar pos ang dis)))
;;; ;;; Position and direction. ;;; (defvar *position* +start-point+) (defvar *direction* 0) ;;; ;;; Drawing commands (predefined primitives). ;;; (defmacro reset () `(progn (setf *position* +start-point+) (setf *direction* 0))) (defmacro pen-up () `(jcall "addColor" *commands* 255 255 255)) (defmacro pen-down () `(jcall "addColor" *commands* 0 0 0)) (defmacro set-angle (alpha) `(setf *direction* ,alpha)) (defmacro turn (alpha) `(set-angle (rem (+ *direction* ,alpha) 360))) (defmacro jump-to (nx ny) `(setf *position* (make-point :x ,nx :y ,ny))) (defmacro move (dist) `(let* ((seg (segment-from-turn-go *position* *direction* ,dist)) (be (segment-begin seg)) (en (segment-end seg))) (jcall "addLine" *commands* (point-x be) (point-y be) (point-x en) (point-y en)) (setf *position* en))) (defmacro turn-and-go (ang dis) `(progn (set-angle ,ang) (move ,dis))) (defmacro right (dist) `(turn-and-go 0 ,dist)) (defmacro left (dist) `(turn-and-go 180 ,dist)) (defmacro up (dist) `(turn-and-go 90 ,dist)) (defmacro down (dist) `(turn-and-go 270 ,dist)) (defmacro down-right (dist) `(turn-and-go 315 ,dist)) (defmacro up-right (dist) `(turn-and-go 45 ,dist)) (defmacro down-left (dist) `(turn-and-go 225 ,dist)) (defmacro up-left (dist) `(turn-and-go 135 ,dist))
Comments: no replies