// // FlowControl.java // /* VisAD system for interactive analysis and visualization of numerical data. Copyright (C) 1996 - 2017 Bill Hibbard, Curtis Rueden, Tom Rink, Dave Glowacki, Steve Emmerson, Tom Whittaker, Don Murray, and Tommy Jasmin. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ package visad; import java.rmi.*; import java.util.StringTokenizer; import visad.browser.Convert; import visad.util.Util; /** FlowControl is the VisAD abstract super-class for controlling Flow display scalars.<P> */ public abstract class FlowControl extends Control { float flowScale; // DRM add 09-Sep-1999 /** Northern Hemisphere orientation for wind barbs */ public static final int NH_ORIENTATION = 0; /** Southern Hemisphere orientation for wind barbs */ public static final int SH_ORIENTATION = 1; int barbOrientation; boolean adjustFlowToEarth = true; boolean HorizontalVectorSlice; boolean VerticalVectorSlice; boolean HorizontalStreamSlice; boolean VerticalStreamSlice; double HorizontalVectorSliceHeight; double HorizontalStreamSliceHeight; private boolean autoScale = false; private ProjectionControlListener pcl = null; /** Streamline flags -------------------------------*/ boolean streamlinesEnabled; float streamlineDensity; float arrowScale; float stepFactor; float packingFactor; float cntrWeight; int n_pass; float reduction; /** Trajectory flags --------------------------------*/ boolean trajectoryEnabled = false; TrajectoryParams trajParams = new TrajectoryParams(); // WLH need Vertical*Slice location parameters /** * Create a FlowControl * @param d DisplayImpl that this is associated with. */ public FlowControl(DisplayImpl d) { super(d); flowScale = 0.02f; HorizontalVectorSlice = false; VerticalVectorSlice = false; HorizontalStreamSlice = false; VerticalStreamSlice = false; barbOrientation = SH_ORIENTATION; // DRM 9-Sept-1999 HorizontalVectorSliceHeight = 0.0; HorizontalStreamSliceHeight = 0.0; streamlinesEnabled = false; streamlineDensity = 1f; arrowScale = 1f; stepFactor = 2f; packingFactor = 1f; cntrWeight = 3f; n_pass = 0; reduction = 1f; adjustFlowToEarth = true; autoScale = false; trajectoryEnabled = false; } /** * Set scale length for flow vectors (default is 0.02f) * @param scale new scale */ public void setFlowScale(float scale) throws VisADException, RemoteException { flowScale = scale; changeControl(true); } /** * Get scale length for flow vectors * @return scale length for flow vectors */ public float getFlowScale() { return flowScale; } /** * Set barb orientation for wind barbs (default is southern hemisphere) * * @param orientation wind barb orientation * (NH_ORIENTATION or SH_ORIENTATION); */ public void setBarbOrientation(int orientation) throws VisADException, RemoteException { // make sure it is one or the other if (orientation == SH_ORIENTATION || orientation == NH_ORIENTATION) barbOrientation = orientation; else throw new VisADException( "Invalid orientation value: " + orientation); changeControl(true); } /** * Get barb orientation for wind barbs * * @return orientation (false = northern hemisphere) */ public int getBarbOrientation() { return barbOrientation; } /** * Get whether values should be adjusted to the earth * * @param adjust true to adjust * @throws VisADException problem setting the value * @throws RemoteException problem setting the value on remote system */ public void setAdjustFlowToEarth(boolean adjust) throws VisADException, RemoteException { adjustFlowToEarth = adjust; changeControl(true); } /** * Get barb orientation for wind barbs * * @return orientation (false = northern hemisphere) */ public boolean getAdjustFlowToEarth() { return adjustFlowToEarth; } /** * Enable/disable showing vectors as streamlines * * @param flag true to display as streamlines * @throws VisADException problem enabling the streamlines * @throws RemoteException problem enabling the streamlines on remote system */ public void enableStreamlines(boolean flag) throws VisADException, RemoteException { streamlinesEnabled = flag; if (trajectoryEnabled && streamlinesEnabled) { trajectoryEnabled = false; } changeControl(true); } /** * Enable/disable showing vectors as trajectories * * @param flag true to display as trajectories * @throws VisADException problem enabling the trajectories * @throws RemoteException problem enabling the trajectories on remote system */ public void enableTrajectory(boolean flag) throws VisADException, RemoteException { trajectoryEnabled = flag; if (trajectoryEnabled && streamlinesEnabled) { streamlinesEnabled = false; } changeControl(true); } /** * Enable/disable showing vectors as trajectories * * @param flag true to display as trajectories * @param tparms TrajectoryParams to apply. Cannot be null * @throws VisADException problem enabling the trajectories * @throws RemoteException problem enabling the trajectories on remote system */ public void enableTrajectory(boolean flag, TrajectoryParams tparms) throws VisADException, RemoteException { if (tparms == null) { throw new VisADException("TrajectoryParams cannot be null"); } trajectoryEnabled = flag; if (trajectoryEnabled && streamlinesEnabled) { streamlinesEnabled = false; } trajParams = new TrajectoryParams(tparms); changeControl(true); } /** * Set the streamline density * @param density the density value * @throws VisADException problem setting the density * @throws RemoteException problem setting the density on remote system */ public void setStreamlineDensity(float density) throws VisADException, RemoteException { streamlineDensity = density; changeControl(true); } /** * Set the streamline arrow size * @param arrowScale the streamline arrow size * @throws VisADException problem setting the arrow scale * @throws RemoteException problem setting the arrow scale on remote system */ public void setArrowScale(float arrowScale) throws VisADException, RemoteException { this.arrowScale = arrowScale; changeControl(true); } /** * Set the streamline step factor * @param stepFactor the streamline step factor * @throws VisADException problem setting the step factor * @throws RemoteException problem setting the step factor on remote system */ public void setStepFactor(float stepFactor) throws VisADException, RemoteException { this.stepFactor = stepFactor; changeControl(true); } /** * Set the streamline packing * @param packing the streamline packing * @throws VisADException problem setting the packing * @throws RemoteException problem setting the packing on remote system */ public void setStreamlinePacking(float packing) throws VisADException, RemoteException { this.packingFactor = packing; changeControl(true); } /** * Set the streamline smoothing * @param cntrWeight the center weight * @param n_pass number of smoothing passes * @throws VisADException problem setting the smoothing * @throws RemoteException problem setting the smoothing on remote system */ public void setStreamlineSmoothing(float cntrWeight, int n_pass) throws VisADException, RemoteException { this.cntrWeight = cntrWeight; this.n_pass = n_pass; changeControl(true); } /** * Set the streamline reduction * @param reduction the streamline reduction * @throws VisADException problem setting the reduction * @throws RemoteException problem setting the reduction on remote system */ public void setStreamlineReduction(float reduction) throws VisADException, RemoteException { this.reduction = reduction; changeControl(true); } /** * Get the status of streamlines * @return true if streamlines are enabled. */ public boolean streamlinesEnabled() { return streamlinesEnabled; } /** * Get the status of streamlines * @return true if streamlines are enabled. */ public boolean trajectoryEnabled() { return trajectoryEnabled; } public TrajectoryParams getTrajectoryParams() { return trajParams; } public void setTrajectoryParams(TrajectoryParams trajParams) throws VisADException, RemoteException { this.trajParams = new TrajectoryParams(trajParams); changeControl(true); } /** * Get the streamline density factor. * @return the streamline density factor. */ public float getStreamlineDensity() { return streamlineDensity; } /** * Get the streamline arrow scale * @return the streamline arrow scale */ public float getArrowScale() { return arrowScale; } /** * Get the streamline step factor * @return the streamline step factor */ public float getStepFactor() { return stepFactor; } /** * Get the streamline packing value * @return the streamline packing value */ public float getStreamlinePacking() { return packingFactor; } /** * Get the streamline smoothing value * @return the streamline smoothing value */ public float[] getStreamlineSmoothing() { return new float[] {cntrWeight, (float) n_pass}; } /** * Get the streamline reduction value * @return the streamline reduction value */ public float getStreamlineReduction() { return reduction; } /** * Get a string that can be used to reconstruct this control later * @return a string representation of this control */ public String getSaveString() { return "" + getFlowScale() + " " + getBarbOrientation() + " " + streamlinesEnabled() + " " + trajectoryEnabled() + " " + getStreamlineDensity() + " " + getArrowScale() + " " + getStepFactor() + " " + getStreamlinePacking() + " " + getStreamlineSmoothing()[0] + " " + getStreamlineSmoothing()[1] + " " + getStreamlineReduction() + " " + getAdjustFlowToEarth() + " " + getAutoScale(); } /** * Reconstruct this control using the specified save string */ public void setSaveString(String save) throws VisADException, RemoteException { if (save == null) throw new VisADException("Invalid save string"); StringTokenizer st = new StringTokenizer(save); if (st.countTokens() < 2) throw new VisADException("Invalid save string"); float scale = Convert.getFloat(st.nextToken()); int orientation = Convert.getInt(st.nextToken()); boolean es = st.hasMoreTokens() ? Convert.getBoolean(st.nextToken()) : streamlinesEnabled(); boolean tes = st.hasMoreTokens() ? Convert.getBoolean(st.nextToken()) : trajectoryEnabled(); float sd = st.hasMoreTokens() ? Convert.getFloat(st.nextToken()) : getStreamlineDensity(); float as = st.hasMoreTokens() ? Convert.getFloat(st.nextToken()) : getArrowScale(); float sf = st.hasMoreTokens() ? Convert.getFloat(st.nextToken()) : getStepFactor(); float sp = st.hasMoreTokens() ? Convert.getFloat(st.nextToken()) : getStreamlinePacking(); float ssc = st.hasMoreTokens() ? Convert.getFloat(st.nextToken()) : getStreamlineSmoothing()[0]; float ssn = st.hasMoreTokens() ? Convert.getFloat(st.nextToken()) : getStreamlineSmoothing()[1]; float sr = st.hasMoreTokens() ? Convert.getFloat(st.nextToken()) : getStreamlineReduction(); boolean af = st.hasMoreTokens() ? Convert.getBoolean(st.nextToken()) : getAdjustFlowToEarth(); boolean asc = st.hasMoreTokens() ? Convert.getBoolean(st.nextToken()) : getAutoScale(); flowScale = scale; barbOrientation = orientation; streamlinesEnabled = es; trajectoryEnabled = tes; streamlineDensity = sd; arrowScale = as; stepFactor = sf; packingFactor = sp; cntrWeight = ssc; n_pass= (int) ssn; reduction = sr; adjustFlowToEarth = af; autoScale = asc; changeControl(true); } /** * Copy the state of a remote control to this control */ public void syncControl(Control rmt) throws VisADException { if (rmt == null) { throw new VisADException("Cannot synchronize " + getClass().getName() + " with null Control object"); } if (!(rmt instanceof FlowControl)) { throw new VisADException("Cannot synchronize " + getClass().getName() + " with " + rmt.getClass().getName()); } FlowControl fc = (FlowControl )rmt; boolean changed = false; if (!Util.isApproximatelyEqual(flowScale, fc.flowScale)) { changed = true; flowScale = fc.flowScale; } if (barbOrientation != fc.barbOrientation) { changed = true; barbOrientation = fc.barbOrientation; } if (HorizontalVectorSlice != fc.HorizontalVectorSlice) { changed = true; HorizontalVectorSlice = fc.HorizontalVectorSlice; } if (VerticalVectorSlice != fc.VerticalVectorSlice) { changed = true; VerticalVectorSlice = fc.VerticalVectorSlice; } if (HorizontalStreamSlice != fc.HorizontalStreamSlice) { changed = true; HorizontalStreamSlice = fc.HorizontalStreamSlice; } if (VerticalStreamSlice != fc.VerticalStreamSlice) { changed = true; VerticalStreamSlice = fc.VerticalStreamSlice; } if (!Util.isApproximatelyEqual(HorizontalVectorSliceHeight, fc.HorizontalVectorSliceHeight)) { changed = true; HorizontalVectorSliceHeight = fc.HorizontalVectorSliceHeight; } if (!Util.isApproximatelyEqual(HorizontalStreamSliceHeight, fc.HorizontalStreamSliceHeight)) { changed = true; HorizontalStreamSliceHeight = fc.HorizontalStreamSliceHeight; } if (streamlinesEnabled != fc.streamlinesEnabled) { changed = true; streamlinesEnabled = fc.streamlinesEnabled; } if (trajectoryEnabled != fc.trajectoryEnabled) { changed = true; trajectoryEnabled = fc.trajectoryEnabled; } if (!Util.isApproximatelyEqual(streamlineDensity, fc.streamlineDensity)) { changed = true; streamlineDensity = fc.streamlineDensity; } if (!Util.isApproximatelyEqual(arrowScale, fc.arrowScale)) { changed = true; arrowScale = fc.arrowScale; } if (!Util.isApproximatelyEqual(stepFactor, fc.stepFactor)) { changed = true; stepFactor = fc.stepFactor; } if (!Util.isApproximatelyEqual(packingFactor, fc.packingFactor)) { changed = true; packingFactor = fc.packingFactor; } if (!Util.isApproximatelyEqual(cntrWeight, fc.cntrWeight)) { changed = true; cntrWeight = fc.cntrWeight; } if (!Util.isApproximatelyEqual(n_pass, fc.n_pass)) { changed = true; n_pass = fc.n_pass; } if (!Util.isApproximatelyEqual(reduction, fc.reduction)) { changed = true; reduction = fc.reduction; } if (autoScale != fc.autoScale) { // changed = true; setAutoScale(fc.autoScale); } if (!trajParams.equals(fc.trajParams)) { changed = true; trajParams = fc.trajParams; } if (changed) { try { changeControl(true); } catch (RemoteException re) { throw new VisADException("Could not indicate that control" + " changed: " + re.getMessage()); } } } /** * Set whether the vector/barb size should scale with display zoom. * @param auto true to enable autoscaling. * @throws VisADException problem setting the autoscaling */ public void setAutoScale(boolean auto) throws VisADException { if (auto == autoScale) return; DisplayImpl display = getDisplay(); DisplayRenderer dr = display.getDisplayRenderer(); MouseBehavior mouse = dr.getMouseBehavior(); ProjectionControl pc = display.getProjectionControl(); if (auto) { pcl = new ProjectionControlListener(mouse, this, pc); pc.addControlListener(pcl); } else { pc.removeControlListener(pcl); } autoScale = auto; try { changeControl(true); } catch (RemoteException e) { } } /** * Get whether the vector/barb size should scale with display zoom. * @return true if autoscaling is enabled. */ public boolean getAutoScale() { return autoScale; } /** * Null the control. Override superclass to remove the autoscaling listener. */ public void nullControl() { try { setAutoScale(false); } catch (VisADException e) { } super.nullControl(); } /** * See if this control equals another * @param o object in question * @return true if they are equal. */ public boolean equals(Object o) { if (!super.equals(o)) { return false; } FlowControl fc = (FlowControl )o; if (!Util.isApproximatelyEqual(flowScale, fc.flowScale)) { return false; } if (barbOrientation != fc.barbOrientation) { return false; } if (HorizontalVectorSlice != fc.HorizontalVectorSlice) { return false; } if (VerticalVectorSlice != fc.VerticalVectorSlice) { return false; } if (HorizontalStreamSlice != fc.HorizontalStreamSlice) { return false; } if (VerticalStreamSlice != fc.VerticalStreamSlice) { return false; } if (!Util.isApproximatelyEqual(HorizontalVectorSliceHeight, fc.HorizontalVectorSliceHeight)) { return false; } if (!Util.isApproximatelyEqual(HorizontalStreamSliceHeight, fc.HorizontalStreamSliceHeight)) { return false; } if (streamlinesEnabled != fc.streamlinesEnabled) { return false; } if (trajectoryEnabled != fc.trajectoryEnabled) { return false; } if (!Util.isApproximatelyEqual(streamlineDensity, fc.streamlineDensity)) { return false; } if (!Util.isApproximatelyEqual(arrowScale, fc.arrowScale)) { return false; } if (!Util.isApproximatelyEqual(stepFactor, fc.stepFactor)) { return false; } if (!Util.isApproximatelyEqual(packingFactor, fc.packingFactor)) { return false; } if (!Util.isApproximatelyEqual(cntrWeight, fc.cntrWeight)) { return false; } if (!Util.isApproximatelyEqual(n_pass, fc.n_pass)) { return false; } if (!Util.isApproximatelyEqual(reduction, fc.reduction)) { return false; } if (autoScale != fc.autoScale) { return false; } if (!trajParams.equals(fc.trajParams)) { return false; } return true; } /** * Clone this control. * @return a clone of this */ public Object clone() { FlowControl fc = (FlowControl )super.clone(); return fc; } /** * A class for listening to changes in the control. */ class ProjectionControlListener implements ControlListener { private boolean pfirst = true; private MouseBehavior mouse; private ProjectionControl pcontrol; private FlowControl flowControl; private double base_scale = 1.0; private float last_cscale = 1.0f; private double base_size = 1.0; ProjectionControlListener(MouseBehavior m, FlowControl s, ProjectionControl p) { mouse = m; flowControl = s; pcontrol = p; } public void controlChanged(ControlEvent e) throws VisADException, RemoteException { double[] matrix = pcontrol.getMatrix(); double[] rot = new double[3]; double[] scale = new double[3]; double[] trans = new double[3]; mouse.instance_unmake_matrix(rot, scale, trans, matrix); if (pfirst) { pfirst = false; base_scale = scale[2]; last_cscale = 1.0f; base_size = flowControl.getFlowScale(); } else { float cscale = (float) (base_scale / scale[2]); float ratio = cscale / last_cscale; if (ratio < 0.95f || 1.05f < ratio) { // 5% change last_cscale = cscale; flowControl.setFlowScale((float) base_size * cscale); } } } } }