//(c) Copyright 2008, Scott Vorthmann. All rights reserved.
package org.vorthmann.zome.app.impl;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.vecmath.Point3d;
import javax.vecmath.Quat4d;
import javax.vecmath.Vector3d;
import com.vzome.core.algebra.AlgebraicField;
import com.vzome.core.algebra.AlgebraicVector;
import com.vzome.core.construction.Point;
import com.vzome.core.editor.DocumentModel;
import com.vzome.core.editor.StrutCreation;
import com.vzome.core.math.Projection;
import com.vzome.core.math.RealVector;
import com.vzome.core.math.symmetry.Axis;
import com.vzome.core.math.symmetry.OrbitSet;
import com.vzome.core.math.symmetry.PlaneOrbitSet;
import com.vzome.core.model.RealizedModel;
import com.vzome.core.render.RenderedModel;
import com.vzome.core.render.RenderingChanges;
import com.vzome.core.render.TransparentRendering;
import com.vzome.desktop.controller.CameraController;
import com.vzome.desktop.controller.ZoneVectorBall;
public class PreviewStrut implements PropertyChangeListener
{
private final RealizedModel model;
private final RenderedModel rendering;
private final ZoneVectorBall zoneBall;
private Point point;
private boolean usingWorkingPlane;
private Axis zone;
private SymmetryController symmetryController;
private LengthController length;
private StrutCreation strut;
private double[] workingPlaneDual = new double[4]; // a GA homogeneous vector for the dual of the working plane
public PreviewStrut( AlgebraicField field, RenderingChanges mainScene, CameraController viewPlatform )
{
rendering = new RenderedModel( field, true );
TransparentRendering transp = new TransparentRendering( mainScene );
rendering .addListener( transp );
model = new RealizedModel( field, new Projection.Default( field ) );
model .addListener( rendering );
zoneBall = new ZoneVectorBall( viewPlatform )
{
@Override
protected void zoneChanged( Axis oldZone, Axis newZone )
{
if ( length != null )
length .removePropertyListener( PreviewStrut .this );
zone = newZone;
if ( newZone == null )
return;
length = symmetryController.orbitLengths.get( newZone .getDirection() );
adjustStrut();
length .addPropertyListener( PreviewStrut .this );
}
};
}
public void startRendering( SymmetryController symmetryController, Point point, AlgebraicVector workingPlaneNormal )
{
this .point = point;
setSymmetryController( symmetryController );
OrbitSet orbits = symmetryController .getBuildOrbits();
usingWorkingPlane = workingPlaneNormal != null;
if ( usingWorkingPlane )
{
orbits = new PlaneOrbitSet( orbits, workingPlaneNormal );
RealVector normal = workingPlaneNormal .toRealVector();
RealVector other = new RealVector( 1d, 0d, 0d );
RealVector v1 = normal .cross( other );
double len = v1 .length();
if ( len < 0.0001d )
{
other = new RealVector( 0d, 1d, 0d );
v1 = normal .cross( other );
}
RealVector v2 = normal .cross( v1 );
// This line-plane intersection comes right out of Vince, GA4CG, p. 196,
// but I had to derive the general forms for the products.
// This part is just computing the plane trivector, and then its dual vector.
RealVector p = this.point.getLocation() .toRealVector();
RealVector q = p .plus( v1 );
RealVector r = p .plus( v2 );
// p ^ q
double e12 = p.x * q.y - q.x * p.y;
double e23 = p.y * q.z - q.y * p.z;
double e31 = p.z * q.x - q.z * p.x;
double e10 = p.x - q.x; // homogeneous 3-vectors have 4th coord == 1
double e20 = p.y - q.y;
double e30 = p.z - q.z;
// ( p ^ q ) ^ r = P
double P_e123 = e12 * r.z + e23 * r.x + e31 * r.y;
double P_e310 = e10 * r.z + e31 * 1d - e30 * r.x;
double P_e320 = e20 * r.z - e30 * r.y - e23 * 1d;
double P_e120 = e12 * 1d + e20 * r.x - e10 * r.y;
// dual of P = e0123 * P
workingPlaneDual[ 0 ] = - P_e123;
workingPlaneDual[ 1 ] = - P_e320;
workingPlaneDual[ 2 ] = P_e310;
workingPlaneDual[ 3 ] = P_e120;
}
this .zone = zoneBall .setOrbits( orbits );
if ( zone == null )
{
length = null;
return;
}
this .length = symmetryController.orbitLengths.get( zone.getDirection() );
adjustStrut();
length .addPropertyListener( this );
}
public void finishPreview( DocumentModel document )
{
if ( length == null )
return;
length .removePropertyListener( this );
strut .undo();
strut = null;
document .createStrut( point, zone, length .getValue() );
point = null;
zone = null;
length = null;
}
public LengthController getLengthModel()
{
return length;
}
private void adjustStrut()
{
if ( strut != null )
strut .undo();
if ( length == null )
return;
strut = new StrutCreation( point, zone, length .getValue(), model );
strut .perform();
}
@Override
public void propertyChange( PropertyChangeEvent evt )
{
// mousewheel ticked over enough to trigger a scale change in the LengthModel
if ( "length" .equals( evt .getPropertyName() ) )
adjustStrut();
}
public void setSymmetryController( SymmetryController symmetryController )
{
this .symmetryController = symmetryController;
rendering .setOrbitSource( symmetryController .getOrbitSource() );
}
public void trackballRolled( Quat4d roll )
{
if ( point != null && ! usingWorkingPlane )
zoneBall .trackballRolled( roll ); // some of these events will trigger the zone change
}
public void workingPlaneDrag( Point3d imagePt, Point3d eyePt )
{
if ( point != null && usingWorkingPlane )
{
RealVector localCenter = this.point.getLocation() .toRealVector();
RealVector s = new RealVector( imagePt.x, imagePt.y, imagePt.z );
RealVector q = new RealVector( eyePt.x, eyePt.y, eyePt.z );
// This line-plane intersection comes right out of Vince, GA4CG, p. 196,
// but I had to derive the general forms for the products.
// s ^ t = l
double e12 = s.x * q.y - q.x * s.y;
double e23 = s.y * q.z - q.y * s.z;
double e31 = s.z * q.x - q.z * s.x;
double e10 = s.x - q.x; // homogeneous 3-vectors have 4th coord == 1
double e20 = s.y - q.y;
double e30 = s.z - q.z;
// dualP . l = x (the result, in homogeneous coordinates)
double x_e1 = - workingPlaneDual[ 2 ] * e12 - workingPlaneDual[ 0 ] * e10 + workingPlaneDual[ 3 ] * e31;
double x_e2 = - workingPlaneDual[ 3 ] * e23 - workingPlaneDual[ 0 ] * e20 + workingPlaneDual[ 1 ] * e12;
double x_e3 = - workingPlaneDual[ 1 ] * e31 - workingPlaneDual[ 0 ] * e30 + workingPlaneDual[ 2 ] * e23;
double x_e0 = workingPlaneDual[ 1 ] * e10 + workingPlaneDual[ 2 ] * e20 + workingPlaneDual[ 3 ] * e30;
Vector3d almostPlanarVector = new Vector3d();
almostPlanarVector .set( x_e1 / x_e0 - localCenter.x,
x_e2 / x_e0 - localCenter.y,
x_e3 / x_e0 - localCenter.z );
zoneBall .setVector( almostPlanarVector );
}
}
}