package org.vorthmann.zome.app.impl;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.image.RenderedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.Writer;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageOutputStream;
import javax.vecmath.Point3d;
import javax.vecmath.Quat4d;
import org.vorthmann.j3d.J3dComponentFactory;
import org.vorthmann.j3d.MouseTool;
import org.vorthmann.j3d.MouseToolDefault;
import org.vorthmann.j3d.MouseToolFilter;
import org.vorthmann.j3d.Trackball;
import org.vorthmann.ui.Controller;
import org.vorthmann.ui.DefaultController;
import org.vorthmann.ui.LeftMouseDragAdapter;
import org.vorthmann.zome.export.java2d.Java2dExporter;
import org.vorthmann.zome.export.java2d.Java2dSnapshot;
import com.vzome.core.algebra.AlgebraicField;
import com.vzome.core.algebra.AlgebraicNumber;
import com.vzome.core.algebra.AlgebraicVector;
import com.vzome.core.algebra.HeptagonField;
import com.vzome.core.algebra.PentagonField;
import com.vzome.core.algebra.RootThreeField;
import com.vzome.core.algebra.RootTwoField;
import com.vzome.core.commands.Command;
import com.vzome.core.commands.Command.Failure;
import com.vzome.core.construction.Construction;
import com.vzome.core.construction.Point;
import com.vzome.core.construction.Polygon;
import com.vzome.core.construction.Segment;
import com.vzome.core.editor.DocumentModel;
import com.vzome.core.editor.SymmetrySystem;
import com.vzome.core.exporters.Exporter3d;
import com.vzome.core.math.Polyhedron;
import com.vzome.core.math.RealVector;
import com.vzome.core.math.symmetry.Axis;
import com.vzome.core.math.symmetry.Direction;
import com.vzome.core.math.symmetry.IcosahedralSymmetry;
import com.vzome.core.math.symmetry.Symmetry;
import com.vzome.core.model.Connector;
import com.vzome.core.model.Manifestation;
import com.vzome.core.model.ManifestationChanges;
import com.vzome.core.model.Panel;
import com.vzome.core.model.Strut;
import com.vzome.core.render.Color;
import com.vzome.core.render.Colors;
import com.vzome.core.render.RenderedManifestation;
import com.vzome.core.render.RenderedModel;
import com.vzome.core.render.RenderedModel.OrbitSource;
import com.vzome.core.render.RenderingChanges;
import com.vzome.core.viewing.Camera;
import com.vzome.core.viewing.Lights;
import com.vzome.core.viewing.ThumbnailRenderer;
import com.vzome.desktop.controller.CameraController;
import com.vzome.desktop.controller.RenderingViewer;
import com.vzome.desktop.controller.ThumbnailRendererImpl;
/**
* @author Scott Vorthmann 2003
*/
public class DocumentController extends DefaultController implements J3dComponentFactory
{
private DocumentModel documentModel;
private final PreviewStrut previewStrut;
private final RenderedModel mRenderedModel;
private RenderedModel currentSnapshot;
private CameraController mViewPlatform;
private Lights sceneLighting;
private RenderingViewer imageCaptureViewer;
private ThumbnailRenderer thumbnails;
private RenderedModel mControlBallModel;
private RenderingChanges mainScene;
private final RenderingChanges mControlBallScene;
private final ApplicationController mApp;
private Java2dSnapshot mSnapshot = null;
private boolean useGraphicalViews = false;
private boolean showStrutScales = false;
private boolean mRequireShift = false;
private boolean drawOutlines = false;
private boolean showFrameLabels = false;
private boolean useWorkingPlane = false;
private final LessonController lessonController;
private final boolean startReader;
private final ManifestationChanges selectionRendering;
private PropertyChangeListener articleChanges, modelChanges;
private final Properties properties;
private SymmetryController symmetryController;
private Segment workingPlaneAxis = null;
private final ToolsController toolsController;
private final PartsController partsController;
private Map<String,SymmetryController> symmetries = new HashMap<>();
private final ClipboardController systemClipboard;
private String designClipboard;
private boolean editingModel;
private Camera currentView;
private MouseTool lessonPageClick, articleModeMainTrackball, modelModeMainTrackball;
private final Component modelCanvas, leftEyeCanvas, rightEyeCanvas;
private MouseTool selectionClick, previewStrutStart, previewStrutRoll, previewStrutPlanarDrag;
private final Controller polytopesController;
private int changeCount = 0;
private final PickingController monoController, leftController, rightController;
/*
* See the javadoc to control the logging:
*
* http://java.sun.com/j2se/1.4.2/docs/api/java/util/logging/LogManager.html
*
* The easiest approach is to edit the lines at the end of the global config
* file:
*
* edit /Library/Java/Home/lib/logging.properties
*/
public DocumentController( DocumentModel document, ApplicationController app, Properties props )
{
setNextController( app );
this .properties = props;
this .documentModel = document;
if ( this .documentModel .isMigrated() )
this .changeCount = -1; // this will force isEdited() to return true
final boolean asTemplate = propertyIsTrue( "as.template" );
final boolean newDocument = propertyIsTrue( "new.document" );
drawOutlines = propertyIsTrue( "outline.geometry" );
startReader = ! newDocument && ! asTemplate;
editingModel = super.userHasEntitlement( "model.edit" ) && ! propertyIsTrue( "reader.preview" );
systemClipboard = propertyIsTrue( "enable.system.clipboard" )
? new ClipboardController()
: null;
toolsController = new ToolsController( document );
toolsController .setNextController( this );
this .addPropertyListener( toolsController );
polytopesController = new PolytopesController( this .documentModel );
polytopesController .setNextController( this );
mRenderedModel = new RenderedModel( this .documentModel .getField(), true );
currentSnapshot = mRenderedModel;
selectionRendering = new ManifestationChanges()
{
@Override
public void manifestationAdded( Manifestation m )
{
mRenderedModel .setManifestationGlow( m, true );
}
@Override
public void manifestationRemoved( Manifestation m )
{
mRenderedModel .setManifestationGlow( m, false );
}
@Override
public void manifestationColored( Manifestation m, Color c ) {}
};
this .documentModel .addSelectionListener( selectionRendering );
this .articleChanges = new PropertyChangeListener()
{
@Override
public void propertyChange( PropertyChangeEvent change )
{
if ( "currentSnapshot" .equals( change .getPropertyName() ) )
{
// contents of old "renderSnapshot" action
RenderedModel newSnapshot = (RenderedModel) change .getNewValue();
if ( newSnapshot != currentSnapshot )
{
synchronized ( newSnapshot ) {
RenderedModel .renderChange( currentSnapshot, newSnapshot, mainScene );
}
currentSnapshot = newSnapshot;
}
}
else if ( "currentView" .equals( change .getPropertyName() ) )
{
Camera newView = (Camera) change .getNewValue();
if ( ! newView .equals( mViewPlatform .getView() ) )
mViewPlatform .restoreView( newView );
}
else if ( "thumbnailChanged" .equals( change .getPropertyName() ) )
{
int pageNum = (Integer) change .getNewValue();
DocumentController .this .documentModel .getLesson() .updateThumbnail( pageNum, DocumentController .this .documentModel, thumbnails );
}
}
};
this .modelChanges = new PropertyChangeListener()
{
@Override
public void propertyChange( PropertyChangeEvent change )
{
switch ( change .getPropertyName() ) {
case "current.edit.xml":
properties() .firePropertyChange( change ); // forward to the UI for display
break;
default:
break;
}
}
};
if ( editingModel )
this .documentModel .addPropertyChangeListener( this .modelChanges );
else
this .documentModel .addPropertyChangeListener( this .articleChanges );
sceneLighting = new Lights( app .getLights() ); // TODO: restore the ability for the document to override
// this seems backwards, I know... the TrackballViewPlatformModel is the main
// model, and only forwards two events to trackballVPM
mViewPlatform = new CameraController( document .getViewModel() );
mViewPlatform .setNextController( this );
mRequireShift = "true".equals( app.getProperty( "multiselect.with.shift" ) );
useGraphicalViews = "true".equals( app.getProperty( "useGraphicalViews" ) );
showStrutScales = "true" .equals( app.getProperty( "showStrutScales" ) );
showFrameLabels = "true" .equals( app.getProperty( "showFrameLabels" ) );
RenderingViewer.Factory rvFactory = app .getJ3dFactory();
mainScene = rvFactory .createRenderingChanges( sceneLighting, true, this );
this .addPropertyListener( (PropertyChangeListener) mainScene );
modelCanvas = rvFactory .createJ3dComponent( "" ); // name not relevant there
imageCaptureViewer = rvFactory.createRenderingViewer( mainScene, modelCanvas );
mViewPlatform .addViewer( imageCaptureViewer );
monoController = new PickingController( imageCaptureViewer, this );
leftEyeCanvas = rvFactory .createJ3dComponent( "" );
RenderingViewer viewer = rvFactory .createRenderingViewer( mainScene, leftEyeCanvas );
mViewPlatform .addViewer( viewer );
viewer .setEye( RenderingViewer .LEFT_EYE );
leftController = new PickingController( viewer, this );
rightEyeCanvas = rvFactory .createJ3dComponent( "" );
viewer = rvFactory .createRenderingViewer( mainScene, rightEyeCanvas );
mViewPlatform .addViewer( viewer );
viewer .setEye( RenderingViewer .RIGHT_EYE );
rightController = new PickingController( viewer, this );
// TODO define a standalone controller class for contextual menus, etc.
Controller controlBallProps = new DefaultController()
{
@Override
public String getProperty( String name )
{
switch ( name ) {
case "drawOutlines":
return "false";
case "showIcosahedralLabels":
if ( super .userHasEntitlement( "developer.extras" )
&& documentModel.getSymmetrySystem().getSymmetry() instanceof IcosahedralSymmetry ) {
return super.getProperty( "trackball.showIcosahedralLabels" );
} else
return "false";
default:
return super.getProperty( name );
}
}
};
controlBallProps .setNextController( this );
mControlBallScene = rvFactory .createRenderingChanges( sceneLighting, true, controlBallProps );
thumbnails = new ThumbnailRendererImpl( rvFactory, sceneLighting );
mApp = app;
AlgebraicField field = this .documentModel .getField();
previewStrut = new PreviewStrut( field, mainScene, mViewPlatform );
lessonController = new LessonController( this .documentModel .getLesson(), mViewPlatform );
lessonController .setNextController( this );
setSymmetrySystem( this .documentModel .getSymmetrySystem() );
// can't do this before the setSymmetrySystem() call just above
if ( mRenderedModel != null )
{
this .documentModel .setRenderedModel( mRenderedModel );
this .currentSnapshot = mRenderedModel; // Not too sure if this is necessary
}
partsController = new PartsController( symmetryController .getOrbitSource() );
partsController .setNextController( this );
mRenderedModel .addListener( partsController );
copyThisView(); // initialize the "copied" view at startup.
}
@Override
public Component createJ3dComponent( String name )
{
if ( name.startsWith( "mainViewer" ) )
{
Component canvas = null;
if ( name .endsWith( "monocular" ) )
canvas = modelCanvas;
else if ( name .endsWith( "leftEye" ) )
canvas = leftEyeCanvas;
else
canvas = rightEyeCanvas;
/*
* Mouse tools here follow some general principles:
*
* 1. Don't try to dispatch events by pipelining tools ("first one eats"). Instead, all tools get the event,
* and let mutually-exclusive conditions make sure that only the desired processing
* occurs. MouseToolFilter is a good way to filter events.
*
* 2. Use LeftMouseDragAdapter whenever you need drag / click hysteresis. Really, the
* only "drag" tool here that does not need that is the targetManifestationDrag,
* since that is not a true drag.
*
* 3. Use mode switch (article/model) to detach and attach sets of tools.
*
* 4. A Trackball can be subclassed to determine what the transform operates on.
*
*/
// these are for the model viewer (article mode)
MouseTool mouseTool = new MouseToolDefault()
{
@Override
public void mouseClicked( MouseEvent e )
{
actionPerformed( new ActionEvent( e .getSource(), ActionEvent.ACTION_PERFORMED, "nextPage" ) );
e .consume();
}
};
if ( canvas == modelCanvas )
lessonPageClick = mouseTool; // will not be attached, initially; gets attached on switchToArticle
mouseTool = new MouseToolFilter( mViewPlatform .getZoomScroller() )
{
@Override
public void mouseWheelMoved( MouseWheelEvent e )
{
LengthController length = previewStrut .getLengthModel();
if ( length != null )
{
// scroll to scale the preview strut (when it is rendered)
length .getMouseTool() .mouseWheelMoved( e );
// don't adjustPreviewStrut() here, let the prop change trigger it,
// so we don't flicker for every tick of the mousewheel
}
else
{
// no strut build in progress, so zoom the view
super .mouseWheelMoved( e );
}
}
};
mouseTool .attach( canvas );
mouseTool = mViewPlatform .getTrackball();
if ( propertyIsTrue( "presenter.mode" ) )
((Trackball) mouseTool) .setModal( false );
// if ( ! editingModel )
// {
// // cannot use MouseTool .attach(), because it attaches a useless wheel listener,
// // and ViewPlatformControlPanel will attach a better one to the parent component
// canvas .addMouseListener( mouseTool );
// canvas .addMouseMotionListener( mouseTool );
// }
if ( canvas == modelCanvas )
articleModeMainTrackball = mouseTool; // will not be attached, initially; gets attached on switchToArticle
// this wrapper for mainCanvasTrackball is disabled when the press is initiated over a ball
mouseTool = new LeftMouseDragAdapter( new MouseToolFilter( articleModeMainTrackball )
{
boolean live = false;
@Override
public void mousePressed( MouseEvent e )
{
RenderedManifestation rm = imageCaptureViewer .pickManifestation( e );
if ( rm == null || !( rm .getManifestation() instanceof Connector ) )
{
this .live = true;
super .mousePressed( e );
}
}
@Override
public void mouseDragged( MouseEvent e )
{
if ( live )
super .mouseDragged( e );
}
@Override
public void mouseReleased( MouseEvent e )
{
this .live = false;
super .mouseReleased( e );
}
} );
if ( canvas == modelCanvas )
modelModeMainTrackball = mouseTool;
if ( editingModel )
modelModeMainTrackball .attach( canvas );
else
articleModeMainTrackball .attach( canvas );
// clicks become select or deselect all
mouseTool = new LeftMouseDragAdapter( new ManifestationPicker( imageCaptureViewer )
{
@Override
protected void manifestationPicked( Manifestation target, boolean shiftKey )
{
mErrors .clearError();
boolean shift = true;
if ( mRequireShift )
shift = shiftKey;
if ( target == null )
try {
documentModel .performAndRecord( documentModel .deselectAll() );
} catch ( Exception e ) {
mErrors .reportError( UNKNOWN_ERROR_CODE, new Object[] { e } );
}
else
documentModel .performAndRecord( documentModel .selectManifestation( target, ! shift ) );
}
} );
if ( editingModel )
mouseTool .attach( canvas );
if ( canvas == modelCanvas )
selectionClick = mouseTool;
// drag events to render or realize the preview strut;
// only works when drag starts over a ball
mouseTool = new LeftMouseDragAdapter( new ManifestationPicker( imageCaptureViewer )
{
@Override
protected void dragStarted( Manifestation target, boolean b )
{
if ( target instanceof Connector )
{
mErrors .clearError();
Point point = (Point) target .getConstructions() .next();
AlgebraicVector workingPlaneNormal = null;
if ( useWorkingPlane && (workingPlaneAxis != null ) )
workingPlaneNormal = workingPlaneAxis .getOffset();
previewStrut .startRendering( symmetryController, point, workingPlaneNormal );
}
}
@Override
protected void dragFinished( Manifestation target, boolean b )
{
previewStrut .finishPreview( documentModel );
}
} );
if ( editingModel )
mouseTool .attach( canvas );
if ( canvas == modelCanvas )
previewStrutStart = mouseTool;
// trackball to adjust the preview strut (when it is rendered)
mouseTool = new LeftMouseDragAdapter( new Trackball()
{
@Override
protected void trackballRolled( Quat4d roll )
{
previewStrut .trackballRolled( roll );
}
} );
if ( editingModel )
mouseTool .attach( canvas );
if ( canvas == modelCanvas )
previewStrutRoll = mouseTool;
// working plane drag events to adjust the preview strut (when it is rendered)
mouseTool = new LeftMouseDragAdapter( new MouseToolDefault()
{
@Override
public void mouseDragged( MouseEvent e )
{
Point3d imagePt = new Point3d();
Point3d eyePt = new Point3d();
imageCaptureViewer .pickPoint( e, imagePt, eyePt );
previewStrut .workingPlaneDrag( imagePt, eyePt );
}
} );
if ( editingModel )
mouseTool .attach( canvas );
if ( canvas == modelCanvas )
previewStrutPlanarDrag = mouseTool;
// mRenderedModel .setFactory( mViewer .getSceneGraphFactory() );
// mRenderedModel .setTopGroup( mViewer .getSceneGraphRoot() );
mViewPlatform .updateViewers();
// currentDesign .render( true, null ); // I think this is not necessary now
return canvas;
}
else if ( name.equals( "controlViewer" ) )
{
MouseTool trackball = mViewPlatform .getTrackball();
RenderingViewer.Factory rvFactory = mApp .getJ3dFactory();
Component canvas = rvFactory .createJ3dComponent( name ); // name not relevant there
// cannot use MouseTool .attach(), because it attaches a useless wheel listener,
// and ViewPlatformControlPanel will attach a better one to the parent component
canvas .addMouseListener( trackball );
canvas .addMouseMotionListener( trackball );
RenderingViewer viewer = rvFactory .createRenderingViewer( mControlBallScene, canvas );
mViewPlatform .addViewer( new TrackballRenderingViewer( viewer ) );
// mControlBallScene .reset();
for ( RenderedManifestation rm : mControlBallModel )
mControlBallScene.manifestationAdded( rm );
mViewPlatform .updateViewers();
return canvas;
}
else
{
RenderingViewer.Factory rvFactory = mApp .getJ3dFactory();
Component canvas = rvFactory .createJ3dComponent( name ); // name not relevant there
drawOutlines = true;
RenderingChanges scene = rvFactory.createRenderingChanges( sceneLighting, true, this );
mRenderedModel.addListener( scene );
RenderingViewer viewer = rvFactory.createRenderingViewer( mainScene, canvas );
this.addPropertyListener( (PropertyChangeListener) viewer );
return canvas;
}
}
private SymmetryController getSymmetryController( String name )
{
SymmetryController result = this .symmetries .get( name );
if ( result == null ) {
result = new SymmetryController( this, this .documentModel .getSymmetrySystem( name ) );
this .symmetries .put( name, result );
}
return result;
}
private void setSymmetrySystem( SymmetrySystem symmetrySystem )
{
String name = symmetrySystem .getName();
symmetryController = getSymmetryController( name );
mControlBallModel = mApp.getSymmetryModel( symmetryController.getSymmetry() );
if ( mControlBallScene != null ) {
mControlBallScene.reset();
for ( RenderedManifestation rm : mControlBallModel )
mControlBallScene.manifestationAdded( rm );
}
mViewPlatform .setSnapper( symmetryController.getSnapper() );
properties().firePropertyChange( "symmetry", null, name ); // notify UI, so cardpanel can flip, or whatever
setRenderingStyle();
}
private void setRenderingStyle()
{
if ( mRenderedModel != null ) {
if ( partsController != null )
partsController .startSwitch( symmetryController .getOrbitSource() );
mRenderedModel .setOrbitSource( symmetryController .getOrbitSource() );
if ( partsController != null )
partsController .endSwitch();
}
if ( previewStrut != null )
previewStrut .setSymmetryController( symmetryController );
}
@Override
public void doAction( String action, ActionEvent e ) throws Failure
{
if ( "finish.load".equals( action ) ) {
boolean openUndone = propertyIsTrue( "open.undone" );
boolean asTemplate = propertyIsTrue( "as.template" );
// used to finish loading a model history on a non-UI thread
this .documentModel .finishLoading( openUndone, asTemplate );
// mainScene is not listening to mRenderedModel yet, so batch the rendering changes to it
this .syncRendering();
return;
}
mErrors .clearError();
try {
if ( action.equals( "undo" ) )
this .documentModel .undo( ! this .userHasEntitlement( "developer.extras" ) );
else if ( action.equals( "redo" ) )
this .documentModel .redo( ! this .userHasEntitlement( "developer.extras" ) );
else if ( action.equals( "undoToBreakpoint" ) ) {
this .documentModel .undoToBreakpoint();
} else if ( action.equals( "redoToBreakpoint" ) ) {
this .documentModel .redoToBreakpoint();
}
else if ( action.equals( "setBreakpoint" ) )
this .documentModel .setBreakpoint();
else if ( action.equals( "undoAll" ) ) {
this .documentModel .undoAll();
} else if ( action.equals( "redoAll" ) ) {
this .documentModel .redoAll( - 1 );
} else if ( action.startsWith( "redoUntilEdit." ) ) {
String editNum = action .substring( "redoUntilEdit.".length() ).trim();
// editNum will be "null" if user canceled the numeric input dialog
// We'll also treat an empty string the same as canceling
if(! (editNum.equals("null") || editNum.equals("") ) ) {
int eNum = -1;
try {
eNum = Integer.parseInt( editNum );
} catch (Exception ex) {
mErrors.reportError( "'" + editNum + "' is not a valid integer. Edit number must be a positive integer.", new Object[] {} );
}
if(eNum <= 0) {
mErrors.reportError( "Edit number must be a positive integer.", new Object[] {} );
} else {
this.documentModel.redoAll(eNum);
}
}
}
else if ( action .equals( "switchToArticle" ) )
{
currentView = mViewPlatform .getView();
selectionClick .detach( modelCanvas );
previewStrutStart .detach( modelCanvas );
previewStrutRoll .detach( modelCanvas );
previewStrutPlanarDrag .detach( modelCanvas );
modelModeMainTrackball .detach( modelCanvas );
lessonPageClick .attach( modelCanvas );
articleModeMainTrackball .attach( modelCanvas );
documentModel .addPropertyChangeListener( this .articleChanges );
documentModel .removePropertyChangeListener( this .modelChanges );
lessonController .doAction( "restoreSnapshot", e );
this .editingModel = false;
properties() .firePropertyChange( "editor.mode", "model", "article" );
}
else if ( action .equals( "switchToModel" ) )
{
documentModel .removePropertyChangeListener( this .articleChanges );
documentModel .addPropertyChangeListener( this .modelChanges );
mViewPlatform .restoreView( currentView );
RenderedModel .renderChange( currentSnapshot, mRenderedModel, mainScene );
currentSnapshot = mRenderedModel;
lessonPageClick .detach( modelCanvas );
articleModeMainTrackball .detach( modelCanvas );
selectionClick .attach( modelCanvas );
previewStrutStart .attach( modelCanvas );
previewStrutRoll .attach( modelCanvas );
previewStrutPlanarDrag .attach( modelCanvas );
modelModeMainTrackball .attach( modelCanvas );
this .editingModel = true;
properties() .firePropertyChange( "editor.mode", "article", "model" );
}
else if ( action .equals( "takeSnapshot" ) )
{
documentModel .addSnapshotPage( mViewPlatform .getView() );
}
else if ( "nextPage" .equals( action ) )
lessonController .doAction( action, e );
// else if ( action .equals( "test.pick.cube" ) ) // just a test of
// // Bounds picking
// {
// Collection rms = imageCaptureViewer.pickCube();
// for ( Iterator it = rms.iterator(); it.hasNext(); ) {
// RenderedManifestation rm = (RenderedManifestation) it.next();
// Manifestation targetManifestation = null;
// if ( rm != null && rm.isPickable() )
// targetManifestation = rm.getManifestation();
// else
// continue;
// document .selectManifestation( targetManifestation, true ); // NOT UNDOABLE!
// }
// }
else if ( action.equals( "refresh.2d" ) )
{
Java2dExporter exporter = new Java2dExporter( mViewPlatform.getView(), this.mApp.getColors(), this.sceneLighting, this.currentSnapshot );
this .mSnapshot .setExporter( exporter );
}
else if ( action.startsWith( "setStyle." ) )
setRenderingStyle();
else if ( action.equals( "toggleFrameLabels" ) )
{
showFrameLabels = ! showFrameLabels;
properties() .firePropertyChange( "showFrameLabels", !showFrameLabels, showFrameLabels );
}
else if ( action.equals( "toggleOutlines" ) )
{
drawOutlines = ! drawOutlines;
properties() .firePropertyChange( "drawOutlines", !drawOutlines, drawOutlines );
}
else if ( action.equals( "toggleWorkingPlane" ) )
{
useWorkingPlane = ! useWorkingPlane;
// if ( useWorkingPlane )
// mainScene .enableWorkingPlane( workingPlaneOrbits );
// else
// mainScene .disableWorkingPlane( workingPlaneOrbits );
}
else if ( action.equals( "toggleOrbitViews" ) ) {
boolean old = useGraphicalViews;
useGraphicalViews = ! old;
properties().firePropertyChange( "useGraphicalViews", old, this.useGraphicalViews );
}
else if ( action.equals( "toggleStrutScales" ) ) {
boolean old = showStrutScales;
showStrutScales = ! old;
properties().firePropertyChange( "showStrutScales", old, this.showStrutScales );
}
else if ( action.startsWith( "setSymmetry." ) ) {
String system = action.substring( "setSymmetry.".length() );
this .documentModel .setSymmetrySystem( system );
setSymmetrySystem( this .documentModel .getSymmetrySystem() );
}
else if ( action.equals( "copyThisView" ) )
{
copyThisView();
}
else if ( action.equals( "useCopiedView" ) )
{
mViewPlatform .useCopiedView();
}
else if ( action.equals( "lookAtOrigin" ) )
mViewPlatform.setLookAtPoint( new Point3d( 0, 0, 0 ) );
else if ( action.equals( "lookAtSymmetryCenter" ) )
{
RealVector loc = documentModel .getParamLocation( "ball" );
mViewPlatform .setLookAtPoint( new Point3d( loc.x, loc.y, loc.z ) );
}
else if ( action .equals( "usedOrbits" ) )
{
Set<Direction> usedOrbits = new HashSet<>();
for ( RenderedManifestation rm : mRenderedModel ) {
Polyhedron shape = rm .getShape();
Direction orbit = shape .getOrbit();
if ( orbit != null )
usedOrbits .add( orbit );
}
symmetryController .availableController .doAction( "setNoDirections", null );
for ( Direction orbit : usedOrbits ) {
symmetryController .availableController .doAction( "enableDirection." + orbit .getName(), null );
}
}
// This was an experiment, to see if the applyQuaternionSymmetry() approach was workable.
// It seems it is too restrictive to insist upon all W=0 inputs.
// else if ( action.equals( "h4symmetry" ) )
// {
// QuaternionicSymmetry qsymm = document .getField() .getQuaternionSymmetry( "H_4" );
// document .applyQuaternionSymmetry( qsymm, qsymm );
// }
//
else if ( action .equals( "delete" ) )
{
documentModel .doEdit( action );
}
else if ( action .equals( "cut" ) )
{
setProperty( "clipboard", documentModel .copySelectionVEF() );
documentModel .doEdit( "delete" );
}
else if ( action .equals( "copy" ) )
setProperty( "clipboard", documentModel .copySelectionVEF() );
else if ( action.equals( "paste" ) )
{
String vefContent = getProperty( "clipboard" );
documentModel .pasteVEF( vefContent );
}
else
{
switch ( action ) {
case "setSymmetryCenter":
this .documentModel .setParameter( null, "ball" );
break;
case "setSymmetryAxis":
this .documentModel .setParameter( null, "strut" );
break;
default:
Symmetry symm = symmetryController .getSymmetry();
String symmName = symm .getName();
AlgebraicField field = symm .getField();
if ( action.equals( "axialsymm" ) )
{
if ( field instanceof RootTwoField )
action += "-roottwo";
else if ( field instanceof RootThreeField )
action += "-rootthree";
else if ( field instanceof HeptagonField )
action += "-heptagon";
else if ( "octahedral" .equals( symmName ) )
action += "-octa";
else if ( "icosahedral" .equals( symmName ) )
action += "-icosa";
} else if ( action.equals( "tetrasymm" ) || action.equals( "octasymm" ) )
if ( field instanceof RootTwoField )
action += "-roottwo";
else if ( field instanceof RootThreeField )
action += "-rootthree";
else if ( field instanceof HeptagonField )
action += "-heptagon";
else
action += "-golden";
boolean handled = documentModel .doEdit( action );
if ( ! handled )
super .doAction( action, e );
}
}
} catch ( Command.Failure failure ) {
// signal an error to the user
mErrors.reportError( USER_ERROR_CODE, new Object[] { failure } );
} catch ( Exception re ) {
Throwable cause = re.getCause();
if ( cause instanceof Command.Failure )
mErrors.reportError( USER_ERROR_CODE, new Object[] { cause } );
else
mErrors.reportError( UNKNOWN_ERROR_CODE, new Object[] { re } );
}
}
private void copyThisView()
{
mViewPlatform.copyView(mViewPlatform.getView());
}
public void syncRendering()
{
if ( mainScene != null )
{
if ( editingModel )
{
RenderedModel .renderChange( new RenderedModel( null, null ), mRenderedModel, mainScene );
mRenderedModel .addListener( mainScene );
// get the thumbnails updating in the background
if ( lessonController != null )
lessonController .renderThumbnails( documentModel, thumbnails );
}
else
try {
currentSnapshot = new RenderedModel( null, null ); // force render of first snapshot, see "renderSnapshot." below
lessonController .doAction( "restoreSnapshot", new ActionEvent( this, 0, "restoreSnapshot" ) );
// order these to avoid issues with the thumbnails (unexplained)
lessonController .renderThumbnails( documentModel, thumbnails );
} catch ( Exception e1 ) {
Throwable cause = e1.getCause();
if ( cause instanceof Command.Failure )
mErrors.reportError( USER_ERROR_CODE, new Object[] { cause } );
else
mErrors.reportError( UNKNOWN_ERROR_CODE, new Object[] { e1 } );
}
}
}
@Override
public void doScriptAction( String command, String script )
{
if ( command.equals( "runZomicScript" )
|| command.equals( "runPythonScript" )
|| command.equals( "import.vef" )
//|| command.equals( "import.zomod" )
)
documentModel .doScriptAction( command, script );
else
super .doScriptAction( command, script );
}
/* (non-Javadoc)
* @see org.vorthmann.ui.DefaultController#doFileAction(java.lang.String, java.io.File)
*/
@Override
public void doFileAction( String command, final File file )
{
// TODO set output file types
try {
final Colors colors = mApp.getColors();
if ( "save".equals( command ) )
{
File dir = file .getParentFile();
if ( ! dir .exists() )
dir .mkdirs();
// A try-with-resources block closes the resource even if an exception occurs
try (FileOutputStream out = new FileOutputStream( file )) {
documentModel .serialize( out, this .properties );
}
// just did a save, so lets record the document change count again,
// so isEdited() will return false until more changes occur.
// IMPORTANT! TODO if we ever implement "save a copy", this code should NOT reset
// the count just because we're writing a copy. The reset will have to move to the
// context of the save.
this .changeCount = this .documentModel .getChangeCount();
return;
}
if ( "capture-animation" .equals( command ) )
{
File dir = file .isDirectory()? file : file .getParentFile();
Dimension size = this .modelCanvas .getSize();
String html = readResource( "org/vorthmann/zome/app/animation.html" );
html = html .replaceFirst( "%%WIDTH%%", Integer .toString( size .width ) );
html = html .replaceFirst( "%%HEIGHT%%", Integer .toString( size .height ) );
File htmlFile = new File( dir, "index.html" );
writeFile( html, htmlFile );
String js = readResource( "org/vorthmann/zome/app/j360-loop.js" );
writeFile( js, new File( dir, "j360-loop.js" ) );
AnimationCaptureController animation = new AnimationCaptureController( this .mViewPlatform, dir );
captureImageFile( null, AnimationCaptureController.TYPE, animation );
this .openApplication( htmlFile );
return;
}
if ( command.startsWith( "capture." ) )
{
final String extension = command .substring( "capture.".length() );
captureImageFile( file, extension, null );
return;
}
// if ( command .equals( "export.zomespace" ) )
// {
// new ZomespaceExporter( file ) .exportArticle( document, colors, sceneLighting, getSaveXml(), getProperty( "edition" ), getProperty( "version" ) );
// } else
if ( command.startsWith( "export." ) )
{
Writer out = new FileWriter( file );
Dimension size = this .modelCanvas .getSize();
try {
String format = command .substring( "export." .length() ) .toLowerCase();
Exporter3d exporter = documentModel .getNaiveExporter( format, mViewPlatform .getView(), colors, sceneLighting, currentSnapshot );
if ( exporter != null ) {
exporter.doExport( file, file.getParentFile(), out, size.height, size.width );
}
else {
exporter = this .mApp .getExporter( format );
if ( exporter == null ) {
// currently just "partgeom"
exporter = documentModel .getStructuredExporter( format, mViewPlatform .getView(), colors, sceneLighting, mRenderedModel );
}
if ( exporter != null )
exporter .doExport( documentModel, file, file.getParentFile(), out, size.height, size.width );
}
} finally {
out.close();
}
this .openApplication( file );
return;
}
if ( command.equals( "import.vef" )
// || command.equals( "import.zomod" )
) {
String vefData = readFile( file );
documentModel .doScriptAction( command, vefData );
return;
}
if ( command.equals( "import.zomecad.binary" ) ) {
// InputStream bytes = new FileInputStream( file );
// new ZomeCADImporter( bytes, events, (PentagonField) mField ) .parseStream();
}
if ( command.equals( "save.pdf" ) ) {
}
super.doFileAction( command, file );
} catch ( Exception e ) {
mErrors.reportError( UNKNOWN_ERROR_CODE, new Object[] { e } );
}
}
private void captureImageFile( final File file, final String extension, final AnimationCaptureController animation )
{
String maxSizeStr = getProperty( "max.image.size" );
final int maxSize = ( maxSizeStr != null )? Integer .parseInt( maxSizeStr ) :
( animation != null )? animation .getImageSize() : -1; // Animation images can't be too big
if ( animation != null ) {
animation .rotate();
}
imageCaptureViewer .captureImage( maxSize, new RenderingViewer.ImageCapture()
{
private void setImageCompression(String format, ImageWriteParam iwParam)
{
if (iwParam.canWriteCompressed()) {
// Ensure that the compressionType we want to use is supported
String[] preferedTypes = null; // Listed in preferred order
switch (format) {
case "BMP":
preferedTypes = new String[]{
"BI_RGB", // BI_RGB seems to result in a smaller file than BI_BITFIELDS in Windows 10
"BI_BITFIELDS", // OK
// "BI_PNG", // File is created OK, but can't be opened by default viewer "Photos" in Windows 10
// "BI_JPEG", // File is created OK, but can't be opened by default viewer "Photos" in Windows 10
// "BI_RLE8", // IOException: Image can not be encoded with compression type BI_RLE8
// "BI_RLE4", // IOException: Image can not be encoded with compression type BI_RLE4
};
break;
case "GIF":
preferedTypes = new String[]{
"lzw",
"LZW",
};
break;
case "JPEG":
preferedTypes = new String[]{
"JPEG",
};
break;
}
if (preferedTypes != null) {
String[] compressionTypes = iwParam.getCompressionTypes();
String chosenType = null;
for (String preferredType : preferedTypes) {
for (String compressionType : compressionTypes) {
if (compressionType.equals(preferredType)) {
chosenType = preferredType;
break;
}
}
if (chosenType != null) {
break;
}
}
if (chosenType != null) {
System.out.println(format + " compression set to " + chosenType);
iwParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
iwParam.setCompressionType(chosenType); // this default is better for BMP, to avoid non-compression
iwParam.setCompressionQuality(.95f);
}
}
}
}
@Override
public void captureImage( final RenderedImage image )
{
String format = extension.toUpperCase();
if ( format.equals( "JPG" ) )
format = "JPEG";
try {
ImageWriter writer = ImageIO.getImageWritersByFormatName( format ) .next();
ImageWriteParam iwParam = writer .getDefaultWriteParam();
this.setImageCompression(format, iwParam);
File thisFile = ( animation != null ) ? animation .nextFile() : file;
// A try-with-resources block closes the resource even if an exception occurs
try (ImageOutputStream ios = ImageIO.createImageOutputStream( thisFile )) {
writer .setOutput( ios );
writer .write( null, new IIOImage( image, null, null), iwParam );
writer .dispose(); // disposing of the writer doesn't close ios
// ios is closed automatically by exiting the try-with-resources block
// either normally or due to an exception
// If this code is ever changed to not use the try-with-resources block
// then uncomment the following line so that ios will be explicitly closed
// ios.close();
}
if ( animation == null )
openApplication( file );
else if ( ! animation .finished() ) {
// queue up the next capture in the sequence
EventQueue .invokeLater( new Runnable(){
@Override
public void run() {
captureImageFile( null, extension, animation );
}});
}
} catch (Exception e) {
mErrors.reportError( UNKNOWN_ERROR_CODE, new Object[] { e } );
}
}
} );
}
private static String readFile( File file ) throws IOException
{
ByteArrayOutputStream out = new ByteArrayOutputStream();
// A try-with-resources block closes the resource even if an exception occurs
try (InputStream bytes = new FileInputStream( file )) {
byte[] buf = new byte[1024];
int num;
while ( ( num = bytes.read( buf, 0, 1024 ) ) > 0 ) {
out.write( buf, 0, num );
}
}
return new String( out.toByteArray() );
}
private static String readResource( String resourcePath )
{
InputStream stream = null;
try {
stream = DocumentController.class .getClassLoader() .getResourceAsStream( resourcePath );
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
int num;
while ( ( num = stream .read( buf, 0, 1024 )) > 0 )
out .write( buf, 0, num );
return new String( out .toByteArray() );
} catch (Exception e) {
return null;
} finally {
if ( stream != null )
try {
stream .close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
private static void writeFile( String content, File file ) throws Exception
{
// A try-with-resources block closes the resource even if an exception occurs
try (FileWriter writer = new FileWriter( file )) {
writer .write( content );
} catch (Exception ex) {
throw ex;
}
}
@Override
public boolean[] enableContextualCommands( String[] menu, MouseEvent e )
{
boolean[] result = new boolean[menu.length];
for ( int i = 0; i < menu.length; i++ ) {
String menuItem = menu[i];
switch ( menuItem ) {
case "lookAtOrigin":
case "lookAtSymmetryCenter":
case "setBackgroundColor":
case "copyThisView":
result[i] = true;
break;
case "useCopiedView":
result[ i ] = mViewPlatform .hasCopiedView();
break;
default:
result[i] = false;
}
}
return result;
}
public boolean isEdited()
{
int currentChangeCount = this .documentModel .getChangeCount();
return currentChangeCount > this .changeCount;
}
@Override
public void setErrorChannel( ErrorChannel errors )
{
mErrors = errors;
if ( mViewPlatform == null )
return;
mViewPlatform.setErrorChannel( errors );
lessonController.setErrorChannel( errors );
toolsController .setErrorChannel( errors );
}
@Override
public String getProperty( String string )
{
if ( "useGraphicalViews".equals( string ) ) {
return Boolean.toString( this.useGraphicalViews );
}
if ( "showStrutScales".equals( string ) ) {
return Boolean.toString( this.showStrutScales );
}
if ( "startReader".equals( string ) )
return Boolean.toString( startReader );
if ( "migrated".equals( string ) )
return Boolean.toString( this .documentModel .isMigrated() );
if ( "edited".equals( string ) )
return Boolean.toString( this .isEdited() );
if ( "symmetry".equals( string ) )
return symmetryController.getSymmetry().getName();
if ( "field.name".equals( string ) )
return this .documentModel .getField() .getName();
if ( "field.label".equals( string ) ) {
String name = this .documentModel .getField() .getName();
return super.getProperty( "field.label." + name ); // defer to app controller
// TODO implement AlgebraicField.getLabel()
}
if ( string .startsWith( "supports.symmetry." ) )
{
String group = string .substring( "supports.symmetry." .length() );
Symmetry symm = this .documentModel .getField() .getSymmetry( group );
return Boolean .toString(symm != null);
}
if ( "clipboard" .equals( string ) )
return systemClipboard != null
? systemClipboard.getClipboardContents()
: designClipboard;
if ( "showFrameLabels" .equals( string ) )
return Boolean .toString( showFrameLabels );
if ( "drawOutlines" .equals( string ) )
return Boolean .toString( drawOutlines );
if ( "useWorkingPlane" .equals( string ) )
return Boolean .toString( useWorkingPlane );
if ( "workingPlaneDefined" .equals( string ) )
return Boolean .toString( workingPlaneAxis != null );
if ( string .startsWith( "tool.description." ) )
{
string = string .substring( "tool.description." .length() ); // strip "tool.description."
if ( "bookmark" .equals( string ) )
return "set the selection to be whatever it is now";
else if ( "module" .equals( string ) )
return "duplicate a module at every point";
else if ( "point reflection" .equals( string ) )
return "apply a central inversion through a point";
else if ( "mirror" .equals( string ) )
return "reflect objects in a mirror plane";
else if ( "translation" .equals( string ) )
return "translate objects by the given offset";
else if ( "linear map" .equals( string ) )
return "apply a linear transformation";
else if ( "rotation" .equals( string ) )
return "rotate objects around an axis, by a fixed angle";
else if ( "scaling" .equals( string ) )
return "scale objects linearly, relative to a fixed point";
else if ( "plane" .equals( string ) )
return "select objects based on incidence on a plane or half-space";
else
return "replicate objects to create " + string + " symmetry";
}
if ( string .startsWith( "file-dialog-title." ) ) {
switch ( string .substring( "file-dialog-title." .length() ) ) {
case "capture-animation":
return "Choose a target folder for animation frames";
default:
break; // fall through to properties or super
}
}
String result = this .properties .getProperty( string );
if ( result != null )
return result;
return super.getProperty( string );
}
@Override
public Controller getSubController( String name )
{
switch ( name ) {
case "monocularPicking":
return monoController;
case "leftEyePicking":
return leftController;
case "rightEyePicking":
return rightController;
case "viewPlatform":
return mViewPlatform;
case "symmetry":
return symmetryController;
case "tools":
return toolsController;
case "parts":
return partsController;
case "polytopes":
return polytopesController;
case "lesson":
return lessonController;
case "snapshot.2d": {
if ( mSnapshot == null ) {
Java2dExporter exporter = new Java2dExporter( mViewPlatform.getView(), this.mApp.getColors(), this.sceneLighting, this.currentSnapshot );
mSnapshot = new Java2dSnapshot( exporter );
mSnapshot .setNextController( this );
}
return mSnapshot;
}
default:
if ( name.startsWith( "symmetry." ) )
return this.symmetries.get( name.substring( "symmetry.".length() ) );
else
return null;
}
}
@Override
public void setProperty( String cmd, Object value )
{
if ( "useGraphicalViews".equals( cmd ) ) {
this.useGraphicalViews = "true".equals( value );
properties().firePropertyChange( cmd, false, this.useGraphicalViews );
return;
} else if ( "visible".equals( cmd ) ) {
// Window is listening, will bring itself to the front, or close itself
// App controller will set topDocument, or remove the document.
properties() .firePropertyChange( "visible", null, value );
} else if ( "name".equals( cmd ) ) {
// App controller is listening, will change its map
properties() .firePropertyChange( "name", null, (String) value );
} else if ( "backgroundColor".equals( cmd ) ) {
sceneLighting .setProperty( cmd, value );
} else if ( "terminating".equals( cmd ) ) {
properties().firePropertyChange( cmd, null, value );
} else if ( "showStrutScales".equals( cmd ) ) {
boolean old = showStrutScales;
this.showStrutScales = "true" .equals( value );
properties().firePropertyChange( "showStrutScales", old, this.showStrutScales );
}
else if ( "clipboard" .equals( cmd ) ) {
if( systemClipboard != null ) {
systemClipboard.setClipboardContents((String) value);
}
else {
designClipboard = (String) value;
}
}
else if ( "showFrameLabels" .equals( cmd ) )
{
boolean old = showFrameLabels;
showFrameLabels = "true" .equals( value );
properties() .firePropertyChange( "showFrameLabels", old, showFrameLabels );
}
super.setProperty( cmd, value );
}
@Override
public String[] getCommandList( String listName )
{
if ( "tool.templates" .equals( listName ) )
{
List<String> all = new ArrayList<>();
List<String> genericTools = Arrays .asList( new String[]{ "translation", "scaling", "point reflection", "linear map", "module", "bookmark", "plane" } );
all .addAll( genericTools );
List<String> symmTools = Arrays .asList( symmetryController .getCommandList( listName ) );
all .addAll( symmTools );
return all .toArray( new String[all.size()] );
}
return super.getCommandList( listName );
}
public void doManifestationAction( Manifestation pickedManifestation, String action )
{
Construction singleConstruction = null;
if ( pickedManifestation != null )
singleConstruction = pickedManifestation .getConstructions().next();
try {
switch ( action ) {
case "undoToManifestation":
this .documentModel .undoToManifestation( pickedManifestation );
break;
// case "symmTool-icosahedral":
// Symmetry symmetry = ((SymmetryController) getSymmetryController( "icosahedral" )) .getSymmetry();
//
// this .document .createTool( "icosahedral.99/", "icosahedral", toolsController, symmetry );
// this .document .createAndApplyTool( pickedManifestation, "icosahedral", toolsController, symmetry );
// break;
case "setSymmetryCenter":
this .documentModel .setParameter( singleConstruction, "ball" );
break;
case "setSymmetryAxis":
this .documentModel .setParameter( singleConstruction, "strut" );
break;
case "setWorkingPlaneAxis":
this .workingPlaneAxis = (Segment) singleConstruction;
this .properties() .firePropertyChange( "workingPlaneDefined", false, true );
break;
case "setWorkingPlane":
this .workingPlaneAxis = this .documentModel .getPlaneAxis( (Polygon) singleConstruction );
this .properties() .firePropertyChange( "workingPlaneDefined", false, true );
break;
case "lookAtBall":
RealVector loc = documentModel .getLocation( singleConstruction );
mViewPlatform .setLookAtPoint( new Point3d( loc.x, loc.y, loc.z ) );
break;
case "setBuildOrbitAndLength": {
AlgebraicVector offset = ((Strut) pickedManifestation) .getOffset();
Axis zone = symmetryController .getZone( offset );
Direction orbit = zone .getOrbit();
AlgebraicNumber length = zone .getLength( offset );
symmetryController .availableController .doAction( "enableDirection." + orbit .getName(), null );
symmetryController .buildController .doAction( "setSingleDirection." + orbit .getName(), null );
LengthController lmodel = (LengthController) symmetryController .buildController .getSubController( "currentLength" );
lmodel .setActualLength( length );
}
break;
case "selectCollinear":
documentModel .selectCollinear( (Strut) pickedManifestation );
break;
case "selectParallelStruts":
documentModel.selectParallelStruts( (Strut) pickedManifestation );
break;
case "selectSimilarSize": {
Strut strut = (Strut) pickedManifestation;
AlgebraicVector offset = strut .getOffset();
Axis zone = symmetryController .getZone( offset );
Direction orbit = zone .getOrbit();
AlgebraicNumber length = zone .getLength( offset );
documentModel .selectSimilarStruts( orbit, length ); // does performAndRecord
}
break;
}
} catch ( Command.Failure failure ) {
// signal an error to the user
mErrors.reportError( USER_ERROR_CODE, new Object[] { failure } );
} catch ( Exception re ) {
Throwable cause = re.getCause();
if ( cause instanceof Command.Failure )
mErrors.reportError( USER_ERROR_CODE, new Object[] { cause } );
else
mErrors.reportError( UNKNOWN_ERROR_CODE, new Object[] { re } );
}
}
public String getManifestationProperty( Manifestation pickedManifestation, String propName )
{
switch ( propName ) {
case "objectProperties":
if ( pickedManifestation != null ) {
boolean devExtras = userHasEntitlement( "developer.extras" );
StringBuffer buf = new StringBuffer();
final NumberFormat FORMAT = NumberFormat .getNumberInstance( Locale .US );
OrbitSource symmetry = symmetryController .getOrbitSource();
Manifestation man = pickedManifestation;
Axis zone = null;
if (man instanceof Connector) {
AlgebraicVector loc = man.getLocation();
if(devExtras) {
System.out.println(loc.getVectorExpression(AlgebraicField.EXPRESSION_FORMAT));
System.out.println(loc.getVectorExpression(AlgebraicField.ZOMIC_FORMAT));
System.out.println(loc.getVectorExpression(AlgebraicField.VEF_FORMAT));
}
buf.append("location: ");
loc.getVectorExpression(buf, AlgebraicField.DEFAULT_FORMAT);
} else if (man instanceof Strut) {
buf.append("start: ");
Strut strut = Strut.class.cast(man);
strut.getLocation().getVectorExpression(buf, AlgebraicField.DEFAULT_FORMAT);
buf.append("\n\noffset: ");
AlgebraicVector offset = strut.getOffset();
if (offset.isOrigin()) {
return "zero length!";
}
if (devExtras) {
System.out.println(offset.getVectorExpression(AlgebraicField.EXPRESSION_FORMAT));
System.out.println(offset.getVectorExpression(AlgebraicField.ZOMIC_FORMAT));
System.out.println(offset.getVectorExpression(AlgebraicField.VEF_FORMAT));
}
offset.getVectorExpression(buf, AlgebraicField.DEFAULT_FORMAT);
buf.append("\n\nnorm squared: ");
AlgebraicNumber normSquared = offset.dot(offset);
double norm2d = normSquared.evaluate();
normSquared.getNumberExpression(buf, AlgebraicField.DEFAULT_FORMAT);
buf.append(" = ");
buf.append(FORMAT.format(norm2d));
zone = symmetry.getAxis(offset);
Direction direction = zone.getDirection();
buf.append("\n\ndirection: ");
if (direction.isAutomatic()) {
buf.append("Automatic ");
}
buf.append(direction.getName());
AlgebraicNumber len = zone.getLength(offset);
len = zone.getOrbit().getLengthInUnits(len);
buf.append("\n\nlength in orbit units: ");
len.getNumberExpression(buf, AlgebraicField.DEFAULT_FORMAT);
if (this .documentModel.getField() instanceof PentagonField) {
buf.append("\n\nlength in Zome b1 struts: ");
if (FORMAT instanceof DecimalFormat) {
((DecimalFormat) FORMAT).applyPattern("0.0000");
}
buf.append(FORMAT.format(Math.sqrt(norm2d) / PentagonField.B1_LENGTH));
}
} else if (man instanceof Panel) {
Panel panel = Panel.class.cast(man);
buf.append("vertices: ");
buf.append(panel.getVertexCount());
String delim = "";
for (AlgebraicVector vertex : panel) {
buf.append(delim);
buf.append("\n ");
vertex.getVectorExpression(buf, AlgebraicField.DEFAULT_FORMAT);
delim = ",";
}
AlgebraicVector normal = panel.getNormal();
buf.append("\n\nnormal: ");
normal.getVectorExpression(buf, AlgebraicField.DEFAULT_FORMAT);
if (devExtras) {
System.out.println(normal.getVectorExpression(AlgebraicField.EXPRESSION_FORMAT));
System.out.println(normal.getVectorExpression(AlgebraicField.ZOMIC_FORMAT));
System.out.println(normal.getVectorExpression(AlgebraicField.VEF_FORMAT));
}
buf.append("\n\nnorm squared: ");
AlgebraicNumber normSquared = normal.dot(normal);
double norm2d = normSquared.evaluate();
normSquared.getNumberExpression(buf, AlgebraicField.DEFAULT_FORMAT);
buf.append(" = ");
buf.append(FORMAT.format(norm2d));
zone = symmetry.getAxis(normal);
Direction direction = zone.getDirection();
buf.append("\n\ndirection: ");
if (direction.isAutomatic()) {
buf.append("Automatic ");
}
buf.append(direction.getName());
} else {
// should never get here
return man.getClass().getSimpleName();
}
if( devExtras) {
if( zone != null) {
buf.append( "\n\nzone: " + zone .toString() );
buf.append( "\n\nrotation: " + zone .getCorrectRotation() );
buf.append( "\n\norientation: " + zone .getOrientation() );
buf.append( "\n\nsense: " + zone .getSense() );
buf.append( "\n\nprototype: " + zone.getDirection().getPrototype() );
// Future TODO: if and when centroid property is eventually added to manifestation
// buf.append( "\n\centroid: " + man .getCentroid() );
}
System .out .println(buf.toString().replace("\n\n", "\n"));
System .out .println();
}
pickedManifestation = null;
return buf.toString();
}
return null;
case "objectColor":
if ( pickedManifestation != null ) {
RenderedManifestation rm = pickedManifestation .getRenderedObject();
String colorStr = rm .getColor() .toString();
pickedManifestation = null;
return colorStr;
}
return null;
default:
return this .getProperty( propName );
}
}
}