/************************************************************************** * Copyright (c) 2001 by Acunia N.V. All rights reserved. * * * * This software is copyrighted by and is the sole property of Acunia N.V. * * and its licensors, if any. All rights, title, ownership, or other * * interests in the software remain the property of Acunia N.V. and its * * licensors, if any. * * * * This software may only be used in accordance with the corresponding * * license agreement. Any unauthorized use, duplication, transmission, * * distribution or disclosure of this software is expressly forbidden. * * * * This Copyright notice may not be removed or modified without prior * * written consent of Acunia N.V. * * * * Acunia N.V. reserves the right to modify this software without notice. * * * * Acunia N.V. * * Vanden Tymplestraat 35 info@acunia.com * * 3000 Leuven http://www.acunia.com * * Belgium - EUROPE * **************************************************************************/ // Author: J. Vandeneede // Created: 2001/03/13 package com.acunia.wonka.test.awt.Graphics; import java.awt.BorderLayout; import java.awt.Button; import java.awt.Checkbox; import java.awt.CheckboxGroup; import java.awt.Color; import java.awt.Container; import java.awt.Dimension; import java.awt.Graphics; import java.awt.GridLayout; import java.awt.Panel; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import com.acunia.wonka.test.awt.VisualTestImpl; import com.acunia.wonka.test.awt.VisualTester; public class FillPoly extends VisualTestImpl implements ActionListener, ItemListener { /****************************************************************/ /** static colors for demo */ private final static Color[] COLORS = {Color.red , Color.orange, Color.yellow, Color.green, new Color(96,96,255), new Color(0,0,128),new Color(128,0,128)}; /** click vincinity */ private final static int VINCINITY=7; /****************************************************************/ /** Variables */ private PolygonPanel blackboard; private Button addBefore; private Button addAfter; private Button delete; private Button mode; private Checkbox fillNative; private Checkbox fillDemo; public FillPoly() { super(); // polygon panel blackboard blackboard = new PolygonPanel(); setLayout(new BorderLayout()); add(blackboard, BorderLayout.CENTER); //buttons CheckboxGroup group = new CheckboxGroup(); Panel buttonrow = new Panel(new GridLayout(2,3)); addBefore = new Button("Add point (before)"); addBefore.addActionListener(this); buttonrow.add(addBefore); addAfter = new Button("Add point (after)"); addAfter.addActionListener(this); buttonrow.add(addAfter); delete = new Button("Delete point"); delete.addActionListener(this); buttonrow.add(delete); mode = new Button((blackboard.isFillMode())?"draw":"fill"); mode.addActionListener(this); buttonrow.add(mode); fillNative = new Checkbox("Native fill", group, true); fillNative.addItemListener(this); buttonrow.add(fillNative); fillDemo = new Checkbox("Demo fill", group, false); fillDemo.addItemListener(this); buttonrow.add(fillDemo); add(buttonrow, BorderLayout.SOUTH); } /** Button pressed: look at the button and either move all selected from red to blue or from blue to red*/ public void actionPerformed(ActionEvent evt) { if(evt.getSource() == addBefore) blackboard.addPoint(true); if(evt.getSource() == addAfter) blackboard.addPoint(false); else if(evt.getSource() == delete ) blackboard.delete(); else if(evt.getSource() == mode) { blackboard.swapFillMode(); mode.setLabel( (blackboard.isFillMode())?"draw":"fill" ); } } /**Checkbox checked: as we know there are only two possibilities: native-fill checked or not, so we don't bother about the ItemEvent data and directly tell the desired fill state to the polygon*/ public void itemStateChanged(ItemEvent evt) { blackboard.setNativeFill(fillNative.getState()); } /** log messages */ public void log(java.awt.Panel p, java.io.Writer w, boolean b)throws java.io.IOException { w.write("DrawOffset "); } public String getHelpText(){ return "Polygon filling demonstration\n\n"+ "The main 'drawing board' allows you to draw an ontline of a geometrical figure. The button lower right button (showing 'fill')"+ " allows you to draw this figure as a filled polygon. (After filling the button's text changes to 'draw'"+ " and pressing this button a second time will display the outlines of the figure again) \n\n"+ " The checkboxes <fill native> and <fill demo> allow you to select wether you want to use the native Graphic.fillPolygon()"+ " or the test's own demo filling algorithm \n\n"+ "You can form the figure by :\n"+ "=> Clicking one of its corner points and dragging it to a new position\n"+ "=> Pressing <add point(before)> and <add point(after)> to add a new corner point before or after the current selected one\n"+ "=> Pressing <delete point> to delete the current corner point"; } public java.awt.Panel getPanel(VisualTester vt) { return this; } public void start(java.awt.Panel p, boolean b){} public void stop(java.awt.Panel p){} static public void main (String[] args) { new FillPoly(); } /****************************************************************/ /****************************************************************/ /** * Inner class to draw the polygon points on the panel */ /****************************************************************/ class PolygonPanel extends Container implements MouseListener, MouseMotionListener { // polygon points private int[] pointx = {2,7,1,5,6}; private int[] pointy = {2,3,4,1,5}; private int currentPoint; private boolean fill = false; private boolean nativeFill = true; // viewport and child coordinates and sizes private Dimension viewport; /**************************************************************/ /** Constructor */ public PolygonPanel() { viewport = new Dimension(); viewport.setSize(9,6); currentPoint = -1; fill = false; this.addMouseListener(this); this.addMouseMotionListener(this); } /**************************************************************/ /** * commands from main screen buttons */ /**************************************************************/ /** add new point before or after the current one */ public void addPoint(boolean before) { if(currentPoint <= 0 && before)//before first point addPoint(pointx.length,(pointx[pointx.length-1]+pointx[0])/2,(pointy[pointx.length-1]+pointy[0])/2);//add last else if (currentPoint<=0) addPoint(1,(pointx[1]+pointx[0])/2,(pointy[1]+pointy[0])/2); else if(currentPoint == pointx.length-1 && !before)//after last point addPoint(pointx.length,(pointx[pointx.length-1]+pointx[0])/2,(pointy[pointx.length-1]+pointy[0])/2);// else if (before) addPoint(currentPoint,(pointx[currentPoint]+pointx[currentPoint-1])/2,(pointy[currentPoint]+pointy[currentPoint-1])/2); else addPoint(currentPoint+1,(pointx[currentPoint]+pointx[currentPoint+1])/2,(pointy[currentPoint]+pointy[currentPoint+1])/2); } /**************************************************************/ /** delete current point */ public void delete() { if(currentPoint>=0 && pointx.length>2) //selection & always at least 2 points delete(currentPoint); } /**************************************************************/ /** fill mode */ public boolean isFillMode() { return fill; } public void swapFillMode() { fill = !fill; this.repaint(); } public void setNativeFill(boolean mode) { nativeFill = mode; if(fill){ this.repaint(); } } /**************************************************************/ /** Auxilliary: insert a point in the points array * (this is build a new set of pointx/pointy buffers containing all points including the current one) */ private void addPoint(int pos, int x, int y) { int[]newx = new int[pointx.length+1]; int[]newy = new int[pointx.length+1]; int i; for(i=0;i<pos;i++) { newx[i]=pointx[i]; newy[i]=pointy[i]; } newx[pos]=x; newy[pos]=y; for(i=pos;i<pointx.length; i++) { newx[i+1]=pointx[i]; newy[i+1]=pointy[i]; } pointx=newx; pointy=newy; currentPoint = pos; this.repaint(); } /**************************************************************/ /** Auxilliary: insert a point in the points array * (this is build a new set of pointx/pointy buffers containing all points except the deleted) */ private void delete(int pos) { int[]newx = new int[pointx.length-1]; int[]newy = new int[pointx.length-1]; int i; for(i=0;i<pos;i++) { newx[i]=pointx[i]; newy[i]=pointy[i]; } for(i=pos;i<newx.length; i++) { newx[i]=pointx[i+1]; newy[i]=pointy[i+1]; } pointx=newx; pointy=newy; currentPoint = -1; this.repaint(); } /**************************************************************/ /** * Mouse listener and mouse motion listener forwards */ /**************************************************************/ /** (on entered, do nothing...) */ public void mouseEntered(MouseEvent e){} /** (on exited, do nothing...) */ public void mouseExited(MouseEvent e){} /** (on moved, do nothing...) */ public void mouseMoved(MouseEvent e){} /** (on clicked, do nothing...) */ public void mouseClicked(MouseEvent e){} /** (on mouse up, do nothing...) */ public void mouseReleased(MouseEvent e){} /** On mouse pressed, find the clicked point */ public void mousePressed(MouseEvent e) { int x = e.getX(); int y = e.getY(); boolean redraw = false; // see if clicked is the current point if(currentPoint>=0) { if(!checkPointClicked(x,y,currentPoint) ) { currentPoint=-1; redraw=true; } } // see if another point is clicked for(int i=0; i<pointx.length && currentPoint<0; i++ ) { if(checkPointClicked(x,y,i)) { currentPoint = i; redraw=true; } } // repaint if needed if(redraw) { this.repaint(); } } /** On mouse dragged, move the current point */ public void mouseDragged(MouseEvent e) { if(currentPoint <0) return; //move current point pointx[currentPoint] = e.getX(); pointy[currentPoint] = e.getY(); this.repaint(); } /**************************************************************/ /** Auxilliary: check if click in vincinity of a given point */ private boolean checkPointClicked(int x, int y, int pointno) { x -=pointx[pointno]; y -=pointy[pointno]; return (x>-VINCINITY && x<VINCINITY && y>-VINCINITY && y<VINCINITY); } /**************************************************************/ /** Auxilliary: blow up initial figure to screen size */ private void setScreen(Dimension newscreen) { System.out.println("Setting new screensize to <"+newscreen.width+", "+newscreen.height+">" ); for(int i=0; i<pointx.length; i++) { pointx[i]=(pointx[i]*newscreen.width)/viewport.width; pointy[i]=(pointy[i]*newscreen.height)/viewport.height; } viewport.setSize(newscreen); } /**************************************************************/ /** Paint */ public void paint(Graphics g) { update(g); } public void update(Graphics g) { //check sizes if(!viewport.equals(this.getSize())) setScreen(this.getSize() ); //viewport g.setColor(Color.white); g.fillRect(1,1, viewport.width-2, viewport.height-2); g.setColor(Color.black); g.drawRect(2,2, viewport.width-4, viewport.height-4); //polygon fill if(fill && nativeFill) { // draw polygon filled using native algorithm g.setColor(Color.blue); g.fillPolygon(pointx,pointy,pointy.length); } else if(fill) { // fill with our own fillPolygonDemo(pointx, pointy, pointy.length, g); } else { //draw points for(int i=0; i<pointx.length; i++) { // line to next point g.setColor(Color.black); for(int j=1; j<pointx.length; j++) g.drawLine(pointx[j],pointy[j],pointx[j-1],pointy[j-1]); g.drawLine(pointx[pointx.length-1],pointy[pointx.length-1],pointx[0],pointy[0]); //last line if(i==currentPoint) { g.setColor(Color.red); g.drawLine(pointx[i]-3,pointy[i]-3,pointx[i]+3,pointy[i]+3); g.drawLine(pointx[i]-3,pointy[i]+3,pointx[i]+3,pointy[i]-3); g.drawLine(pointx[i]-5,pointy[i],pointx[i]+5,pointy[i]); g.drawLine(pointx[i],pointy[i]-5,pointx[i],pointy[i]+5); } else { g.setColor((i>0)?Color.black:Color.blue); g.drawLine(pointx[i]-5,pointy[i],pointx[i]+5,pointy[i]); g.drawLine(pointx[i],pointy[i]-5,pointx[i],pointy[i]+5); } } } } // end of inner class PolygonPanel } /****************************************************************/ /****************************************************************/ /****************************************************************/ /** Our own temporary fill-polygon algorithm in Java rewrite this algorithm in c and paste it into graphics.c in file open-wonka/awt/rudolph/src/native/com/acunia/wonka as soon as it works */ /****************************************************************/ /****************************************************************/ void fillPolygonDemo(int[] pointx, int[] pointy, int size, Graphics g) { // linked list buffer for downward ordening of the corner points int firstpoint; int[] nextpoint = new int[size]; // linestart and linestop buffers for the linepieces forming the boundaries of the piece of polygon currently drawn // (as the piece of polygon that is drawn shifts downwards, the boundaries change dynamically) int[] linestarts = new int[size]; int[] linestops = new int[size]; int numberoflines; // data buffer and linked list buffer for the draw from->to nodes of the scanline currently drawn int[] nodes = new int[size]; int[] nextnode = new int[size]; // step 1: sort all points in a linked list firstpoint = sortIncreasing(pointy, nextpoint, size); //step2: build the first calculation situation : two lines down from top // step3: from the starting point on,build the linepieces array and fill the piece of polygon up to the next lower point int currentpoint, previous, next; int scanline, scanstart, scanstop; int i; currentpoint = firstpoint; numberoflines = 0; int linepiece=0; // debug only do{ // step3a: build the line list for this piece of polygon scanstart = pointy[currentpoint]; scanstop = pointy[nextpoint[currentpoint]]; previous = (currentpoint>0)? currentpoint-1: size-1; next = (currentpoint<(size-1))? currentpoint+1: 0; // if line this->previous goes down, add it if(pointy[previous]>pointy[currentpoint]){ linestarts[numberoflines]=currentpoint; linestops[numberoflines++]=previous; } else if(pointy[previous]<pointy[currentpoint]){ for(i=0; (linestarts[i]!=previous || linestops[i]!=currentpoint); i++); linestarts[i]=linestarts[--numberoflines]; linestops[i]=linestops[numberoflines]; } // if line this->next goes down, add it if(pointy[next]>pointy[currentpoint]){ linestarts[numberoflines]=currentpoint; linestops[numberoflines++]=next; } else if(pointy[next]<pointy[currentpoint]){ // if it goes up, delete it for(i=0; (linestarts[i]!=next || linestops[i]!=currentpoint); i++); linestarts[i]=linestarts[--numberoflines]; linestops[i]=linestops[numberoflines]; } // step3b: fill the piece determined by the line list and the start and stop line g.setColor(COLORS[linepiece%7]); for(scanline=scanstart; scanline<scanstop; scanline++) { // get the nodes for the intersections of the scanlines with the current page for(i=0; i<numberoflines; i++){ //nodes[i]=findIntersection(scanline, pointx[linestarts[i]], pointy[linestarts[i]], pointx[linestops[i]], pointy[linestops[i]]); nodes[i] = pointx[linestarts[i]]; if(scanline > pointy[linestarts[i]]) { nodes[i] += (pointx[linestops[i]]-pointx[linestarts[i]]) * (scanline-pointy[linestarts[i]]) / (pointy[linestops[i]]-pointy[linestarts[i]]); } } // sort them in a linked list previous = sortIncreasing(nodes, nextnode, numberoflines); // draw from node to next node as long as there are while(previous>=0){ next = nextnode[previous]; g.drawLine(nodes[previous],scanline, nodes[next],scanline); previous = nextnode[next]; } } linepiece++; currentpoint = nextpoint[currentpoint]; } while(nextpoint[currentpoint]>=0); // check g.setColor(Color.black); g.drawPolygon(pointx, pointy, size); } /****************************************************************/ /**our own linked-list implementation: * value[] the values to be sorted, * order[] the linked list to be returned * returns the first element of the liinked list */ private int sortIncreasing(int[] value, int[] order, int size){ int first = 0; order[first] = -1; int previous; for(int i=1; i<size; i++){ //add next if(value[i]<=value[first]){ order[i]=first; first = i; } else { previous = first; while(order[previous]>=0 && value[order[previous]]<=value[i]){ previous = order[previous]; } order[i]=order[previous]; order[previous]=i; } } return first; } }