package LDraw.Support;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.TreeSet;
import javax.media.opengl.GL2;
import javax.swing.undo.UndoManager;
import Command.LDrawColor;
import Command.LDrawLine;
import Command.LDrawQuadrilateral;
import Command.LDrawTriangle;
import Common.Box2;
import Common.Box3;
import Common.Matrix3;
import Common.Matrix4;
import Common.Ray3;
import Common.Vector2f;
import Common.Vector3f;
import LDraw.Files.LDrawContainer;
import LDraw.Files.LDrawFile;
import LDraw.Files.LDrawMPDModel;
import LDraw.Files.LDrawModel;
import LDraw.Files.LDrawStep;
import LDraw.Support.type.CacheFlagsT;
import LDraw.Support.type.MessageT;
import Renderer.ILDrawCollector;
import Renderer.ILDrawRenderer;
//==============================================================================
//
//File: LDrawDirective.h
//
//Purpose: This is an abstract base class for all elements of an LDraw
// document.
//
//Created by Allen Smith on 2/19/05.
//Copyright 2005. All rights reserved.
//==============================================================================
/**
* /////////////////////////////////////////////////////////////////////////////
* /// // //OBSERVABLE/OBSERVER PROTOCOLS FOR DIRECTIVES //
* /////////////////////////////////////////////////////////// The observer
* protocol builds a one-way DAG out of our directives allowing directives to
* note changes in their child directives and manage cached data appropriately.
* The protocol rules:
*
* 1. An observer/observable relationship is a pair of _weak_ references. No
* retain counts are maintained, and it is always possible that either party
* could end the relationship by dying. Observers who have good reason why their
* observables should not go away (or vice versa) should maintain retain counts
* separately as part of a separate parallel structure; this is only for message
* flow.
*
* 2. An observer begins observation by requesting that the observable at it to
* an internal hypothetical observer list. Observables do not start the
* relationship.
*
* Similarly, an observer ends observation by requesting its observable to
* remove me from the list.
*
* 3. Death: if the observer dies first, it is responsible for terminating the
* relationship in the usual way by calling removeObserver on its observable
* with itself as the direct object.
*
* But if the observable dies first (while being observed) it sends a
* "goodbye cruel world" message to all observers currently watching it. Those
* observers note that the observable is no longer, um, observable but they do
* _not_ need to call back with a removeObservable message.
*
* The method receiveMessage is used to send a set of specific messages to all
* observing. This is for one-time, relatively rare, non-deallocation events
* that happen.
*
* Observables maintain a bit-field of flags about the status of cachable
* information; an invalidate cache message is sent to all observers once each
* time cachable info is changed until _any_ external caller reads that
* property. (When a caller reads the property, the cache is rebuilt and a new
* invalidate message will be generated.)
*
* CACHING BEHAVIORS
*
* The idea behind the caching flags is this: an observer that produces the sum
* or union from many observables can benefit from knowing that none of the
* observables has changed. (E.g. it's nice for a step to know that no bricks
* have moved.) The correct caching behavior is this:
*
* - Every time the observer reads an observable's property, the observable
* clears the flag for that property, because the observer and observable are
* now in sync. If the property requires expensive computation in the
* observable, the observable probably updates its own internal cache.
*
* - Every time the observable changes that property, it sends a notification
* only IF the cache flag is clear; it then sets the cache flag.
*
* - An observer who receives an invalidate message may in turn invalidate its
* own cache (if necessary), causing a cascade up the observation tree.
*
* Thus if the position of an object is changed 8 times between any external
* code reading the object, an inval message is sent to observers only once. See
* invalCache and revalCache for more details.
*/
/**
* @Class LDrawDirective
*
* @Purpose This is an abstract base class for all elements of an LDraw
* @Represent LDrawDirective.(h, m) of Bricksmith
*
* @author funface2
* @since 2014-03-13
*
*/
public class LDrawDirective implements ILDrawObservable, ILDrawObserver,
Comparable<LDrawDirective>, Cloneable {
/**
* @uml.property name="enclosingDirective"
* @uml.associationEnd inverse="containedObjects:LDraw.Files.LDrawContainer"
*/
protected LDrawContainer enclosingDirective; // LDraw files are a hierarchy.
/**
* @uml.property name="observers"
* @uml.associationEnd multiplicity="(1 1)"
*/
private LDrawFastSet<ILDrawObserver> observers;
/**
* @uml.property name="invalFlags"
* @uml.associationEnd readOnly="true"
*/
private HashMap<CacheFlagsT, Boolean> invalFlags;
/**
* @uml.property name="isSelected"
*/
boolean isSelected;
/**
* @uml.property name="iconName"
*/
String iconName;
// ========== init
// ==============================================================
//
// Purpose: Start me up. This should be called before any other subclass
// initialization code.
//
// ==============================================================================
/**
* @Purpose Start me up. This should be called before any other subclass
* initialization code.
*/
public LDrawDirective() {
init();
}
// ========== init
// ==============================================================
//
// Purpose: Return the base icon name for this type of LDraw directive
//
// ==============================================================================
public static String defaultIconName() {
return null;
}
// ========== init
// ==============================================================
//
// Purpose: Start me up. This should be called before any other subclass
// initialization code.
//
// ==============================================================================
public LDrawDirective init() {
enclosingDirective = null;
iconName = "";
observers = new LDrawFastSet<ILDrawObserver>();
invalFlags = new HashMap<CacheFlagsT, Boolean>();
invalFlags.put(CacheFlagsT.CacheFlagBounds, true);
invalFlags.put(CacheFlagsT.DisplayList, true);
invalFlags.put(CacheFlagsT.ContainerInvalid, true);
return this;
}
// Initialization
// ========== initWithLines:inRange:
// ============================================
//
// Purpose: Convenience method to perform a blocking parse operation
//
// ==============================================================================
public LDrawDirective initWithLines(ArrayList<String> lines, Range range) {
LDrawDirective directive = null;
DispatchGroup group = null;
group = new DispatchGroup();
try {
directive = (LDrawDirective) initWithLines(lines, range, group);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
group._wait();
group._release();
return directive;
}
// ========== initWithLines:inRange:parentGroup:
// ================================
//
// Purpose: Returns the LDraw directive based on lineFromFile, a single line
// of LDraw code from a file.
//
// This method is intended to be overridden by subclasses.
// LDrawDirective's implementation simply returns a useless empty
// directive.
//
// A subclass implementation would look something like:
// ---------------------------------------------------------------
//
// Class LineTypeClass = [LDrawUtilities
// classForDirectiveBeginningWithLine:lineFromFile];
// // Then initialize whatever subclass we came up with for this line.
//
// ==============================================================================
public LDrawDirective initWithLines(ArrayList<String> lines, Range range,
DispatchGroup parentGroup) throws Exception {
if (lines.size() == 0) {
return null;
}
return this;
}
// ---------- rangeOfDirectiveBeginningAtIndex:inLines:maxIndex:
// ------[static]--
//
// Purpose: Returns the range from the first to the last LDraw line of the
// directive which starts at index.
//
// This is a core method of the LDraw parser. It allows supporting
// multiline directives and parallelization in parsing.
//
// Parameters: index - Index of first line to be considered for the
// directive
// lines - (Potentially) All the lines of the enclosing file. The
// directive is represented by a subset of the lines in
// the range between index and maxIndex.
// maxIndex- Index of the last line which could possibly be part of
// the directive.
//
// Notes: Subclasses of LDrawDirective override this method. You should
// ALWAYS call this method on a subclass. Find the subclass using
// +[LDrawUtilities classForDirectiveBeginningWithLine:].
//
// ------------------------------------------------------------------------------
public static Range rangeOfDirectiveBeginningAtIndex(int index,
ArrayList<String> lines, int maxIndex) {
// Most LDraw directives are only one line. For those that aren't the
// subclass should override this method and perform its own parsing.
return new Range(index, 1);
}
// ========== collectColor:viewScale:parentColor:
// =======================================
//
// Purpose: Issues the OpenGL code necessary to draw this element.
//
// This method is intended to be overridden by subclasses.
// LDrawDirective's implementation does nothing.
//
// ==============================================================================
// Directives
public void collectColor() {
// subclasses should override this with OpenGL code to draw the line.
}
// ========== collectSelf:
// ========================================================
//
// Purpose: Collect self is called on each directive by its parents to
// accumulate _mesh_ data into a display list for later drawing.
// The collector protocol passed in is some object capable of
// remembering the collectable data.
//
// Notes: As a general rule, directives that participate in display lists
// need to re-validate their display list cache bit when this is
// called so that the next data edit can move the DL state to
// invalid. (Once data is invalid, further invalidations are
// ignored.)
//
// This requirement falls on both real mesh-participants like
// LDrawTriangle but also their direct containers like LDrawSteps
// and LDrawTextures.
//
// ================================================================================
public void collectSelf(ILDrawCollector renderer) {
// Default implementation collects...nothing.
}
// ========== boundingBox3
// ======================================================
//
// Purpose: return the bounds (in model space) of the directive.
//
// Notes: This routine is cached - the observers have a flag for whether
// bounding box is invalidated. Thus implementations that have a
// sane bounding box should call revalCache before returning a
// value.
//
// Directives that don't have spatial meaning (e.g. hidden
// directives and comments) can return InvalidBox.
//
// ==============================================================================
public Box3 boundingBox3() {
return Box3.getInvalidBox();
}
// ========== debugDrawboundingBox
// ==============================================
//
// Purpose: Draw a translucent visualization of our bounding box to test
// bounding box caching.
//
// Notes: The base class draws the geometry; derived classes can add
// iteration to sub-directives and transforms.
//
// The calling code gets us into our GL state ahead of time.
//
// ==============================================================================
public void debugDrawboundingBox(GL2 gl2) {
Box3 my_bounds = boundingBox3();
Vector3f max = my_bounds.getMax();
Vector3f min = my_bounds.getMin();
if (min.getX() <= max.getX() && min.getY() <= max.getY()
&& min.getZ() <= max.getZ()) {
float verts[] = { min.getX(), min.getY(), min.getZ(), min.getX(),
min.getY(), max.getZ(), min.getX(), max.getY(), max.getZ(),
min.getX(), max.getY(), min.getZ(),
max.getX(), min.getY(), min.getZ(), max.getX(), min.getY(),
max.getZ(), max.getX(), max.getY(), max.getZ(), max.getX(),
max.getY(), min.getZ(),
min.getX(), min.getY(), min.getZ(), min.getX(), max.getY(),
min.getZ(), max.getX(), max.getY(), min.getZ(), max.getX(),
min.getY(), min.getZ(),
min.getX(), min.getY(), max.getZ(), min.getX(), max.getY(),
max.getZ(), max.getX(), max.getY(), max.getZ(), max.getX(),
min.getY(), max.getZ(),
min.getX(), min.getY(), min.getZ(), min.getX(), min.getY(),
max.getZ(), max.getX(), min.getY(), max.getZ(), max.getX(),
min.getY(), min.getZ(),
min.getX(), max.getY(), min.getZ(), min.getX(), max.getY(),
max.getZ(), max.getX(), max.getY(), max.getZ(), max.getX(),
max.getY(), min.getZ() };
gl2.glVertexPointer(3, GL2.GL_FLOAT, 0, FloatBuffer.wrap(verts));
gl2.glDrawArrays(GL2.GL_QUADS, 0, 24);
}
}
public void compareRange(float[] range, Vector3f point) {
if (range[0] > point.x)
range[0] = point.x; // minx
if (range[1] < point.x)
range[1] = point.x; // maxx
if (range[2] > point.z)
range[2] = point.z; // minz
if (range[3] < point.z)
range[3] = point.z; // maxz
if (range[4] > point.y)
range[4] = point.y; // bottom
if (range[5] < point.y)
range[5] = point.y; // top
}
public void getRange(Matrix4 transform, float[] range) {
}
// Hit testing primitives
// ========== hitTest:transform:viewScale:boundsOnly:creditObject:hits:
// =======
//
// Purpose: Tests the directive and any of its children for intersections
// between the pickRay and the directive's drawn content.
//
// Parameters: pickRay - in world coordinates
// transform - transformation to apply to directive points to get
// to world coordinates
// scaleFactor - the window zoom level (1.0 == 100%)
// creditObject - object which should get credit if the
// current object has been hit. (Used to credit nested
// geometry to its parent.) If nil, the hit object credits
// itself.
// hits - keys are hit objects. Values are NSNumbers of hit depths.
//
// ==============================================================================
public void hitTest(Ray3 pckRay, Matrix4 transform,
LDrawDirective creditObject, HashMap<LDrawDirective, Float> hits) {
// subclasses should override this with hit-detection code
}
// ========== boxTest:transform:boundsOnly:creditObject:hits:
// ===================
//
// Purpose: Tests the directive and any of its children for intersections
// between the directive's drawn form and the bounding box in the
// XY plane, after perspective divide.
//
// Parameters: bounds - the box to test against, in post-projection (clip)
// coordinates
// transform - transformation to apply to directive points to get
// to clip coordinates - perspective divide is required!
// creditObject - object which should get credit if the
// current object has been hit. (Used to credit nested
// geometry to its parent.) If nil, the hit object credits
// itself.
// hits - a set of hit directives that we have accumulated so far
// this routine adds more as found.
//
// Return: This function returns true if the _credit object_ was added to
// the set. This allows hierarchies below the credit object to
// early-exit.
//
// Notes: This test is used to do marquee selection - the marquee is
// converted back from viewport to clip coordinates, and then
// the primitive is forward-transformed to clip coordinates, for a
// simple 2-d screen-space test.
//
// My original attempt to implement this used world-space clip
// planes but it is surprisingly expensive to intersect two 3-d
// polygons in arbitrary space. By working in screen space we
// ensure that the selection box is an axis-aligned bounding box,
// which greatly simplifies the algorithm.
//
// (To catch the case where the marquee is fully inside the interior
// of the primitive, in screen space, but using 3-d primitives, we
// have to calculate the union of two convex polyhedra. That's not
// that hard but it requires memory allocations...2-d is much
// simpler.)
//
// ==============================================================================
public boolean boxTest(Box2 bounds, Matrix4 transform, boolean boundsOnly,
LDrawDirective creditObject, TreeSet<LDrawDirective> hits) {
// subclasses should override this with hit-detection code
return false;
}
// ==========
// depthTest:inBox:transform:creditObject:bestObject:bestDepth:=======
//
// Purpose: depthTest finds the closest primitive (in screen space)
// overlapping a given point, as well as its device coordinate
// depth.
//
// Parameters: pt - the 2-d location (in screen space) to intersect.
// inBox - a bounding box in XY (in screen space) surrounding the
// test point. The size of the box (e.g. how much bigger
// it is than the point) defines the "slop" for testing
// infinitely thin primitives like lines and drag handles.
// transform - a model view and projection matrix to transform from
// the directive's model coordinates to screen space.
// creditObject - if not nil, we credit this object with the hit;
// otherwise we use self.
// bestObject - a ptr to an object that is rewritten with the new
// best object if one is found.
// depth - a ptr to a depth (in normalized device coordinates: -1
// is max near, 1 is max far) of that best object. If a
// hit is recorded, depth is updated.
//
// Notes: Depth testing uses "replace if closer" semantics to provide
// return results; thus bestDepth should be initialized to point
// to 1.0f (the far clip plane) before being called. The bounding
// box needs to be enough bigger than the hit point to provide a
// few pixels of slop. The depth should be measured at the hit
// point.
//
// ==============================================================================
public void depthTest(Vector2f testPt, Box2 bounds, Matrix4 transform,
LDrawDirective creditObject, ArrayList<LDrawDirective> bestObject,
FloatBuffer bestDepth) {
// subclasses should override this.
}
// ========== write
// =============================================================
//
// Purpose: Returns the LDraw code for this directive, which can then be
// written out to a LDraw file and read by any LDraw interpreter.
//
// This method is intended to be overridden by subclasses.
// LDrawDirective's implementation does nothing.
//
// ==============================================================================
public String write() {
// Returns a representation of the line which can be written out to a
// file.
return ""; // empty string; subclasses should override this method.
}
// Display
// ========== browsingDescription
// ===============================================
//
// Purpose: Returns a representation of the directive as a short string
// which can be presented to the user.
//
// ==============================================================================
public String browsingDescription() {
return this.getClass().getName();
}
// ========== iconName
// ==========================================================
//
// Purpose: Returns the name of image file used to display this kind of
// object.
//
// ==============================================================================
public String iconName() {
if (this.iconName != null)
return iconName;
else
return "";
}
// ========== inspectorClassName
// ================================================
//
// Purpose: Returns the name of the class used to inspect this one.
//
// ==============================================================================
public String inspectorClassName() {
return "";
}
// Accessors
// ========== ancestors
// =========================================================
//
// Purpose: Returns the ancestors enclosing this directive (as well as the
// directive itself), with the oldest ancestor (highest node) at
// the first index.
//
// ==============================================================================
public ArrayList<LDrawDirective> ancestors() {
ArrayList<LDrawDirective> ancestors = new ArrayList<LDrawDirective>(3);
LDrawDirective currentAncestor = this;
while (currentAncestor != null) {
ancestors.add(0, currentAncestor);
currentAncestor = currentAncestor.enclosingDirective();
}
return ancestors;
}
// ========== enclosingDirective
// ================================================
//
// Purpose: Bricksmith imposes a rigid hierarchy on the data in a file:
//
// LDrawFile
// |
// |-----> LDrawMPDModels
// |
// |-----> LDrawSteps
// |
// |-----> LDrawParts
// |
// |-----> LDraw Primitives
// |
// |-----> LDrawMetaCommands
//
// With the exception of LDrawFile at the root, all directives
// must be enclosed within another directive. This method returns
// the directive in which this one is stored.
//
// Notes: LDrawFiles return nil.
//
// ==============================================================================
public LDrawContainer enclosingDirective() {
return enclosingDirective;
}
// ========== enclosingFile
// =====================================================
//
// Purpose: Returns the highest LDrawFile which contains this directive, or
// nil if the directive is not in the hierarchy of an LDrawFile.
//
// ==============================================================================
public LDrawFile enclosingFile() {
LDrawDirective currentAncestor = this;
boolean foundIt = false;
while (currentAncestor != null) {
if (LDrawFile.class.isInstance(currentAncestor)) {
foundIt = true;
break;
}
currentAncestor = currentAncestor.enclosingDirective();
}
if (foundIt == true)
return (LDrawFile) currentAncestor;
else
return null;
}
// ========== enclosingModel
// ====================================================
//
// Purpose: Returns the highest LDrawModel which contains this directive, or
// nil if the directive is not in the hierarchy of an LDrawModel.
//
// ==============================================================================
public LDrawModel enclosingModel() {
LDrawDirective currentAncestor = this;
boolean foundIt = false;
while (currentAncestor != null) {
if (LDrawModel.class.isInstance(currentAncestor)) {
foundIt = true;
break;
}
currentAncestor = currentAncestor.enclosingDirective();
}
if (foundIt == true)
return (LDrawModel) currentAncestor;
else
return null;
}
// ========== enclosingStep
// =====================================================
//
// Purpose: Returns the highest LDrawStep which contains this directive, or
// nil if the directive is not in the hierarchy of an LDrawStep.
//
// ==============================================================================
public LDrawStep enclosingStep() {
LDrawDirective currentAncestor = this;
boolean foundIt = false;
while (currentAncestor != null) {
if (LDrawStep.class.isInstance(currentAncestor)) {
foundIt = true;
break;
}
currentAncestor = currentAncestor.enclosingDirective();
}
if (foundIt == true)
return (LDrawStep) currentAncestor;
else
return null;
}
// ========== isSelected
// ========================================================
//
// Purpose: Returns whether this directive thinks it's selected.
//
// ==============================================================================
/**
* @return
* @uml.property name="isSelected"
*/
public boolean isSelected() {
return isSelected;
}
// ========== setEnclosingDirective:
// ============================================
//
// Purpose: Just about all directives can be nested inside another one, so
// this is where this method landed.
//
// ==============================================================================
/**
* @param newParent
* @uml.property name="enclosingDirective"
*/
public void setEnclosingDirective(LDrawContainer newParent) {
enclosingDirective = newParent;
}
// ========== setSelected:
// ======================================================
//
// Purpose: Somebody make this a protocol method.
//
// ==============================================================================
public void setSelected(boolean flag) {
isSelected = flag;
}
// ========== setIconName:
// ======================================================
//
// Purpose: Set the icon name
//
// ==============================================================================
/**
* @param icon
* @uml.property name="iconName"
*/
public void setIconName(String icon) {
iconName = icon;
}
// protocol Inspectable
// ========== lockForEditing
// ====================================================
//
// Purpose: Provide thread-safety for this object during inspection.
//
// ==============================================================================
public void lockForEditing() {
enclosingFile().lockForEditing();
}
// ========== unlockEditor
// ======================================================
//
// Purpose: Provide thread-safety for this object during inspection.
//
// ==============================================================================
public void unlockEditor() {
enclosingFile().unlockEditor();
};
// Utilities
// ========== containsReferenceTo:
// ==============================================
//
// Purpose: Overridden by subclasses to indicate if the object (or any of
// its potential children) references a model with the given name.
//
// ==============================================================================
public boolean containsReferenceTo(String name) {
return false;
}
// ========== description
// =======================================================
//
// Purpose: Overrides NSObject method to get a more meaningful description
// suitable for printing to the console.
//
// ==============================================================================
public String description() {
return new String(this.getClass().getName() + "\r\n" + write());
}
// ========== flattenIntoLines:triangles:quadrilaterals:other:currentColor:
// =====
//
// Purpose: Appends the directive (or a copy of the directive) into the
// appropriate container.
//
// Notes: This is used to flatten a complicated hiearchy of primitives and
// part references to files containing yet more primitives into a
// single flat list, which may be drawn to produce a shape visually
// identical to the original structure. The flattened structure,
// however, has the advantage that it is much faster to traverse
// during drawing.
//
// This is the core of -[LDrawModel optimizeStructure].
//
// ==============================================================================
public void flattenIntoLines(ArrayList<LDrawLine> lines,
ArrayList<LDrawTriangle> triangles,
ArrayList<LDrawQuadrilateral> quadriaterals,
ArrayList<LDrawDirective> everythingElse, LDrawColor parentColor,
Matrix4 transform, Matrix3 normalTransform, boolean recursive) {
// By default, a directive does not add itself to the list, an
// indication
// that it is not drawn. Subclasses override this routine to add
// themselves
// to the appropriate list.
}
// ========== isAncestorInList:
// =================================================
//
// Purpose: Given a list of LDrawContainers, returns YES if any of the
// containers is a direct ancestor of the receiver. An ancestor is
// specified by enclosingDirective; each enclosingDirective can
// also have an ancestor. This method searchs the whole chain.
//
// Note: I think this method is potentially buggy. Shouldn't we be doing
// pointer equality tests?
//
// ==============================================================================
public boolean isAncestorInList(ArrayList<LDrawContainer> containers) {
LDrawDirective ancestor = this;
boolean foundInList = false;
do {
ancestor = ancestor.enclosingDirective;
foundInList = containers.contains(ancestor);
} while (ancestor != null && foundInList == false);
return foundInList;
}
// ========== noteNeedsDisplay
// ==================================================
//
// Purpose: An object can certainly be displayed in multiple views, and we
// don't really care to find out which ones here. So we just post
// a notification, and anyone can pick that up.
//
// ==============================================================================
public void noteNeedsDisplay() {
// [[NSNotificationCenter defaultCenter]
// postNotificationName:LDrawDirectiveDidChangeNotification
// object:self];
}
// ========== registerUndoActions:
// ==============================================
//
// Purpose: Registers the undo actions that are unique to this subclass,
// not to any superclass.
//
// ==============================================================================
public void registerUndoActions(UndoManager undoManager) {
// LDrawDirectives are fairly abstract, so all undoable attributes come
// from subclasses.
}
// ========== addObserver:
// ========================================================
//
// Purpose: Adds a directive as an observer of _this_ directive. Implements
// the observable protocol.
//
// ================================================================================
public void addObserver(ILDrawObserver observer) {
observers.add(observer);
}
// ========== removeObserver:
// ========================================================
//
// Purpose: Removes an observer that was watching us for notifications.
// Implements the observable protocol.
//
// ================================================================================
public void removeObserver(ILDrawObserver observer) {
observers.remove(observer);
}
// ========== drawSelf:
// ===========================================================
//
// Purpose: Draw this directive and its subdirectives by calling APIs on
// the passed in renderer, then calling drawSelf on children.
//
// Notes: The drawSelf API draws existing DLs and changes GL state; some
// directives will, as part of drawing, (re)build their DLs on the
// fly. Thus VBO build-up is a by-product of drawing a frame where
// the DL is needed. So we don't actually build VBOs for all models
// on document-open - only the ones we can see!
//
// ================================================================================
public void drawSelf(GL2 gl2, ILDrawRenderer renderer) {
// Default implementation does ... nothing.
}
// These methods should really be "protected" methods for sub-classes to use
// when acting like observables.
// Obj-C doesn't give us compiler-level support to stop externals from
// calling them.
// ============ sendMessageToObservers
// ==========================================
//
// Purpose: This is a utility to send a message to every observer.
// Subclasses use it to reach observers since the observer
// set is private.
//
// ==============================================================================
protected void sendMessageToObservers(MessageT msg) // Send a specific
// message to all
// observers.
{
for (ILDrawObserver observer : observers.getAllItems()) {
observer.receiveMessage(msg, this);
}
}
// ============ invalCache
// ======================================================
//
// Purpose: This is a utility that marks the cache flags as invalid for a
// given subset of flags. If the flags were not already dirty,
// observers are notified.
//
// Usage: Observables should call invalCache with the flag for a bit of
// data EVERY TIME that data changes. Most of the time this will
// result in a no-op or a small quantity of messages. The
// internals take care of tracking cached state.
//
// ==============================================================================
protected void invalCache(CacheFlagsT flags) // Invalidate cache bits - this
// notifies observers as
// needed. Flags are the
// bits to invalidate, not
// the net effect.
{
boolean flagState = false;
if (invalFlags.containsKey(flags) == true)
flagState = invalFlags.get(flags);
invalFlags.put(flags, true);
if (flagState == false) {
for (ILDrawObserver observer : observers.getAllItems()) {
observer.statusInvalidated(flags, this);
}
}
}
// ============== revalCache
// ====================================================
//
// Purpose: This is a utility that clears out cache flags. Clients call
// this when they rebuild their own cached data as it is queried
// by clients.
//
// Return: The function returns the flags that were previously dirty from
// among the set specified.
//
// Usage 1: For an observable that does not need to cache its internals:
// The observable should call this with the flag for the data when
// the accessor is called. This "re-arms" inval notifications for
// observers.
//
// Usage 2: For an observable that uses a cache with lazy rebuilding for a
// property:
// The observer should call revalCache with the flag for the
// property. Then IF the return is the flag passed in, it should
// rebuild the cache. finally, it should return the cache.
//
// In case 2, the cache is being lazily rebuilt when needed and
// notifications rearmed at the same time.
//
// ==============================================================================
protected CacheFlagsT revalCache(CacheFlagsT flags) // Revalidate flags - no
// notifications are
// sent, but internals
// are updated. Returns
// which flags _were_
// dirty.
{
CacheFlagsT were_dirty = invalFlags.get(flags) ? flags : null;
invalFlags.put(flags, false);
return were_dirty;
}
public LDrawMPDModel activeModel() {
// TODO Auto-generated method stub
return null;
}
@Override
public void observableSaysGoodbyeCruelWorld(
ILDrawObservable doomedObservable) {
// TODO Auto-generated method stub
}
@Override
public void statusInvalidated(CacheFlagsT flag, ILDrawObservable observable) {
// TODO Auto-generated method stub
}
@Override
public void receiveMessage(MessageT msg, ILDrawObservable observable) {
// TODO Auto-generated method stub
}
public void insertDirective(LDrawDirective directive, int index) {
// TODO Auto-generated method stub
}
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public int compareTo(LDrawDirective arg0) {
return this.hashCode() - arg0.hashCode();
}
}