/* ToDo: (For all of base and decendants) - Move floodfill to Node class? .. probably when static methods available in library. - Convert type to int? .. so can index into ALists. ..check 1.5 enums to see if they are ints. - Check out Beans as way to set/get properties. - Add shapes to Base class: point, rect, circle, ... . Maybe simply create a shape class with size and location, using "has-a" delegation - "Location": Should all Bases have an x,y,z? Edge: center. Maybe part of Shape? . Note lenTo(Node n) copied to Agent. - Checkout new netlogo, especially new edges but also all basic commands. - Try using Base b rather than Object o in Map and others. But only when -> library, see Filter note - Change c (color) to color? Can preprocessor handle it? [apparently not, heck.] - "Here": Nodes & Edges are "places" while agents are "actors". Should we have a new . "place" subclass of Base so that we can add patches etc too? Agents would now . have either a node or edge or patch or ... in their constructor. - Build HashMap extension like AList .. AMap? - Type: maybe should be int? Then easy to build ALists indexed by type. - showLabel boolean? .. would allow setting labels at construction time but turn on/off. - separate labelColor? .. hmm..could have "invisible" (alpha=0) color? which would fix above too. - Currently we sort by ID .. could sort by any supplied Key value. - Should nodes have both in/out edges? Current scheme (otherNode() etc) are a bit clumsy. . Also, we may need directed edges some time. - Add breed. Is type == breed? . Remove AList arg and create afterwards from breed? - Should any of the Graph utils be moved to Base, AList or Util? - Floodfill could be node method: floodfill from "me". Relies on len() returning weighted value. - Could have a Key for comparable rather than assuming ID. */ int NextBaseID = 0; class Base implements Comparable { static final String AgentsHere = "AgentsHere"; static final String Color = "Color"; static final String Label = "Label"; static final String Type = "Type"; static final String Subtype = "Subtype"; static final String ID = "ID"; color c; String label, type, subtype = ""; int id; float x=0.0, y=0.0, z=0.0; HashMap data = new HashMap(); AList agentsHere = null; Base here = null; boolean showLabel = true; Base(color c, String label, String type, AList a) { setColor(c); setLabel(label); setType(type); setID(NextBaseID++); //this.a = a; a.add(this); } void setID(int id) { this.id = id; setData(ID, id); } int getID() { return id; } void setColor(color c) { this.c = c; setData(Color, c); } color getColor() { return c; } void setLabel(String label) { this.label = label; setData(Label, label); } String getLabel() { return label; } void setShowLabel(boolean showLabel) { this.showLabel = showLabel; } void setType(String type) { this.type = type; setData(Type, type); } String getType() { return type; } void setSubType(String type) { this.subtype = type; setData(Subtype, type); } String getSubtype() { return subtype; } void paintLabel(int align, float x, float y, float z) { if(showLabel && label.length()!=0) { fill(c); // REMIND: Use textcolor variable. textAlign(align); textSize(0.25); billboard.BeginBillboard(x,y,z); text(label,0,0,0); billboard.EndBillboard(); } } void moveBy(float dx, float dy, float dz){ //this.x += dx; this.y += dy; this.z += dz; moveTo(x+dx, y+dy, z+dz); //REMIND: single x,y,z setting allows stashing in Data easier } float dist(Base b1, Base b2) { return sqrt(sq(b1.x-b2.x) + sq(b1.y-b2.y) + sq(b1.z-b2.z)); } void moveToward(float nx, float ny, float nz, float maxStep) { float dx = nx - x; float dy = ny - y; float dz = nz - z; float stepSize = sqrt(sq(dx) + sq(dy) + sq(dz)); if (stepSize > maxStep) { float scale = (maxStep/stepSize); dx *= scale; dy *= scale; dz *= scale; } moveBy(dx, dy, dz); } void moveTo(float x, float y, float z){ this.x = x; this.y = y; this.z = z; } float lenTo(Base b) { // copied fron node, probably should be Base. return sqrt(sq(b.x-x)+sq(b.y-y)+sq(b.z-z)); } //If we build an actor class, these go there. void setHere(Base b) { if(here != null) here.removeAgentHere(this); here = b; if(here != null) here.addAgentHere(this); } //If we build a Place class, these go there private void checkHere() { if(agentsHere==null) { agentsHere=new AList(); setData(AgentsHere,agentsHere); } } AList getAgentsHere() { checkHere(); return agentsHere; } void addAgentHere(Base b) { checkHere(); agentsHere.add(b); } boolean removeAgentHere(Base b) { return agentsHere.remove(b); //return indicator of whether or not b was in here? } boolean isAgentHere(Base b) { return !(agentsHere==null) && agentsHere.indexOf(b)!=-1; } void setData(Object K, Object value) { data.put(K, value); } void unsetData(Object K) { data.remove(K); } void setDataIndirect(Object K, Object K1) { data.put(K, getData(K1)); } void appendData(Object K, Object V, boolean indirect) { // add data to list in data map Object VV = indirect?getData(V):V; ArrayList a = (ArrayList)getData(K); if(a==null) { a = new ArrayList(); setData(K,a); } a.add(VV); } void setDataMinIndexOf(Object K, Object KArray) { setData(K, minIndexOf((ArrayList)getData(KArray))); } void setDataMaxIndexOf(Object K, Object KArray) { setData(K, maxIndexOf((ArrayList)getData(KArray))); } //REMIND: Remove w/ 1.5 & autoboxing. void setData(Object K, int value) { data.put(K, new Integer(value)); } void setData(Object K, float value) { data.put(K, new Float(value)); } void setData(Object K, boolean value) { data.put(K, Boolean.valueOf(value)); // heck, there really are just two, right!? see javadocs. } Object getData(Object K) { return data.get(K); } Object getData(Object K, int i) { return getListData(K).get(i); } int getIntData(Object K, int i) { return ((Integer)getListData(K).get(i)).intValue(); } float getFloatData(Object K, int i) { return ((Float)getListData(K).get(i)).floatValue(); } String getStringData(Object K, int i) { return (String)getListData(K).get(i); } boolean getBooleanData(Object K, int i) { return ((Boolean)getListData(K).get(i)).booleanValue(); } int getIntData(Object K) { return ((Integer)data.get(K)).intValue(); } float getFloatData(Object K) { return ((Float)data.get(K)).floatValue(); } String getStringData(Object K) { return (String)data.get(K); } boolean getBooleanData(Object K) { return ((Boolean)data.get(K)).booleanValue(); } ArrayList getListData(Object K) { return (ArrayList)data.get(K); } String toString() { return "[ID:"+id+" Label:\""+label+"\" Type:\""+type+"\" Color:"+hex(c)+ //" Data: "+data+ // gets java.lang.StackOverflowError, apparently self-refential "]"; } int compareTo(Object o) { Integer myID = (Integer)getData(ID), oID = (Integer)((Base)o).getData(ID); return myID.compareTo(oID); } } interface Filter { boolean filter(Object o); // note: cant use filter(Base b) due to Base being inner class. } interface Map { Object map(Object o); } interface Ask { void ask(Object o); } // Filter used like this: // AList test = Edges.with(new Filter(){boolean filter(Object o) {return ((Base)o).id % 100 == 0;}}); // Map used like this: // AList test = Nodes.map(new Map(){Object map(Object o) {return ((Base)o).label;}}); // Ask used like this: // Nodes.ask(new Ask(){void ask(Object o) {((Base)o).setData("Distance", 2.3f);}}); // Note: Object o can be replaced by Base b if we use static inner classes or regular java classes. class AList extends ArrayList { // Netlogo-like array list. Items must be Base or subclass. AList() { super(); } AList(Collection a) { super(a); } Base get(int i) { return (Base)super.get(i); } Base first() { return get(0); } Base last() { return get(size()-1); } Base randomOneOf() { return get(randomInt(size())); } AList randomNOf(int n) { // note: not sorted by ID. use myAlist.randomNof().sort() if needed. ArrayList b = new ArrayList(this); java.util.Collections.shuffle(b); return new AList(b.subList(0,n)); } Base minOneOf(Comparator comp) { return (Base)Collections.min(this, comp); } Base minOneOf(final Object K) { // final OK here .. changes w/ each call. return this.minOneOf(new Comparator(){ int compare(Object o1, Object o2) { Comparable c1 = (Comparable)((Base)o1).getData(K); Comparable c2 = (Comparable)((Base)o2).getData(K); return c1.compareTo(c2); } } ); } Base maxOneOf(Comparator comp) { return (Base)Collections.max(this, comp); } Base maxOneOf(final Object K) { // final OK here .. changes w/ each call. return this.maxOneOf(new Comparator(){ int compare(Object o1, Object o2) { Comparable c1 = (Comparable)((Base)o1).getData(K); Comparable c2 = (Comparable)((Base)o2).getData(K); return c1.compareTo(c2); } } ); } AList with(Object K, Object V, int sign, boolean compVal) { AList l = new AList(); for(int i = 0; i