<commit_before>package com.jme.scene;

import java.io.IOException;
import java.io.Serializable;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.logging.Logger;
import java.util.logging.Level;

import com.jme.scene.TriMesh;
import com.jme.animation.SkinNode;
import com.jme.scene.Controller;
import com.jme.scene.Geometry;
import com.jme.scene.Node;
import com.jme.math.Quaternion;
import com.jme.math.Vector3f;
import com.jme.scene.state.RenderState;
import com.jme.renderer.Renderer;
import com.jme.util.export.StringFloatMap;
import com.jme.util.export.InputCapsule;
import com.jme.util.export.JMEExporter;
import com.jme.util.export.JMEImporter;
import com.jme.util.export.OutputCapsule;
import com.jme.util.geom.BufferUtils;
import com.jme.util.export.ListenableStringFloatMap;

/**
 * A MorphingGeometry implementation for TriMesh component morph Geometries.
 * <P>
 * Only component TriMeshes of mode Triangles are supported.
 * <P> </P>
 * The base morph must have &gt;= vertexes as the other morphs.
 * </P>
 *
 * TODO:  Optimize the instantiation procedure.  It's pretty complicated to get
 * to reconstitute or instantiate local and delegated influences properly
 * without invoking expensive but unnecessary forceMorphs.
 *
 * @see #MorphingGeometry
 * @author <NAME> (<EMAIL> dot simpson at admc dot com)
 */
public class MorphingTriMesh extends TriMesh implements MorphingGeometry {
    static final long serialVersionUID = 90398457851313109L;
    private static final Logger logger =
            Logger.getLogger(MorphingTriMesh.class.getName());

    protected Map<TriMesh, FloatBuffer> extrapolatedVertBuf;
    protected Map<TriMesh, FloatBuffer> extrapolatedNormBuf;
    protected List<TriMesh> morphs = new ArrayList<TriMesh>();
    protected List<String> morphKeys = new ArrayList<String>();
    // Not using an ordered Map of some type for these paired lists, only
    // because of the atrocious J2SE support for Maps which "preserve" order
    // but do not "apply" an order (I.e. to just preserve the order in which
    // the items are added).
    protected TriMesh baseMorph;
    volatile private boolean dirty = true;
    protected boolean needExtrapolation;

    /**
     * @return the extrapolated vertexBuffer for the specified Morph, if one
     * was generated.  Otherwise, return the traditional vert buffer for it.
     */
    protected FloatBuffer getMorphVertBuffer(TriMesh m) {
        return (extrapolatedVertBuf == null
                || !extrapolatedVertBuf.containsKey(m))
                ? m.getVertexBuffer() : extrapolatedVertBuf.get(m);
    }

    /**
     * @return the extrapolated normBuffer for the specified Morph, if one
     * was generated.  Otherwise, return the traditional norm buffer for it.
     */
    protected FloatBuffer getMorphNormBuffer(TriMesh m) {
        return (extrapolatedNormBuf == null
                || !extrapolatedNormBuf.containsKey(m))
                ? m.getNormalBuffer() : extrapolatedNormBuf.get(m);
    }

    /** This one may be a reference to a remotely managed map */
    protected ListenableStringFloatMap morphInfluencesMap;

    /** This one is persisted with this MorphingTriMesh instance */
    protected ListenableStringFloatMap localMorphInfluencesMap;

    /**
     * @see #MorphingGeometry#getMorphInfluencesMap()
     */
    public ListenableStringFloatMap getMorphInfluencesMap() {
        return morphInfluencesMap;
    }

    /**
     * @see #MorphingGeometry#setMorphInfluencesMap(ListenableStringFloatMap)
     */
    public void setMorphInfluencesMap(ListenableStringFloatMap m) {
        if (localMorphInfluencesMap == m) return;
        if (localMorphInfluencesMap != null)
            localMorphInfluencesMap.removeListener(this);
        localMorphInfluencesMap = m;
        localMorphInfluencesMap.addListener(this, morphKeys);
        morphInfluencesMap = localMorphInfluencesMap;
    }

    /**
     * @see #MorphingGeometry#setSingleMorphInfluence(String, float)
     */
    public void setSingleMorphInfluence(String morphKey, float influence) {
        if (morphInfluencesMap == null)
            throw new IllegalStateException("No morphInfluences set");
        morphInfluencesMap.put(morphKey, influence);
    }

    /**
     * @see #MorphingGeometry#setMorphInfluences(
     *           Map<? extends String, ? extends Float>)
     */
    public void setMorphInfluences(Map<? extends String, ? extends Float> m) {
        if (morphInfluencesMap == null)
            throw new IllegalStateException("No morphInfluences set");
        morphInfluencesMap.putAll(m);
    }

    /**
     * @see #MorphingGeometry#delegateInfluences()
     */
    public boolean delegateInfluences() {
        ListenableStringFloatMap newInfluencesMap = null;
        Node ancestor = null;
        for (ancestor = getParent(); ancestor != null;
                ancestor = ancestor.getParent()) {
            logger.log(
                    Level.FINEST, "Trying anc ''{0'}'...", ancestor.getName());
            if (!(ancestor instanceof MorphInfluencesMapProvider)) continue;
            newInfluencesMap =
                ((MorphInfluencesMapProvider) ancestor).getMorphInfluencesMap();
            if (newInfluencesMap != null) break;
        }
        if (newInfluencesMap == null) {
            logger.info("Failed to find a parent node to delegate to");
            return false;
        }
        if (morphInfluencesMap == newInfluencesMap) {
            logger.fine("The first MorphInfluencesMapProvider found has the "
                    + "same MorphInfluencesMap that we are already using");
            return false;
        }
        morphInfluencesMap = newInfluencesMap;
        morphInfluencesMap.addListener(this, morphKeys);
        logger.log(Level.INFO, "Delegating influences to ''{1}''", ancestor);
        return true;
    }

    /**
     * Constructor for internal use only.
     */
    public MorphingTriMesh() { }

    /**
     * Normal constructor.
     *
     * For local influence control, you should run <PRE><CODE>
     *     morphingTriMesh.addMorph(...);... // anytime before morph()ing
     *     morphingTriMesh.setMorphInfluencesMap(
     *             new ListenableStringFloatMap());
     *     morphingTriMesh.setMorphInfluences(...);
     *     morphingTriMesh.morph();
     * </CODE></PRE>
     * before attaching to a live scene.
     * For remote influence control, if you know there is an available<commit_msg>Fixed my own compounded typing mistakes.  Arg.<commit_after>package com.jme.scene;

import java.io.IOException;
import java.io.Serializable;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.logging.Logger;
import java.util.logging.Level;

import com.jme.scene.TriMesh;
import com.jme.animation.SkinNode;
import com.jme.scene.Controller;
import com.jme.scene.Geometry;
import com.jme.scene.Node;
import com.jme.math.Quaternion;
import com.jme.math.Vector3f;
import com.jme.scene.state.RenderState;
import com.jme.renderer.Renderer;
import com.jme.util.export.StringFloatMap;
import com.jme.util.export.InputCapsule;
import com.jme.util.export.JMEExporter;
import com.jme.util.export.JMEImporter;
import com.jme.util.export.OutputCapsule;
import com.jme.util.geom.BufferUtils;
import com.jme.util.export.ListenableStringFloatMap;

/**
 * A MorphingGeometry implementation for TriMesh component morph Geometries.
 * <P>
 * Only component TriMeshes of mode Triangles are supported.
 * <P> </P>
 * The base morph must have &gt;= vertexes as the other morphs.
 * </P>
 *
 * TODO:  Optimize the instantiation procedure.  It's pretty complicated to get
 * to reconstitute or instantiate local and delegated influences properly
 * without invoking expensive but unnecessary forceMorphs.
 *
 * @see #MorphingGeometry
 * @author <NAME> (blaine dot simpson at admc dot com)
 */
public class MorphingTriMesh extends TriMesh implements MorphingGeometry {
    static final long serialVersionUID = 90398457851313109L;
    private static final Logger logger =
            Logger.getLogger(MorphingTriMesh.class.getName());

    protected Map<TriMesh, FloatBuffer> extrapolatedVertBuf;
    protected Map<TriMesh, FloatBuffer> extrapolatedNormBuf;
    protected List<TriMesh> morphs = new ArrayList<TriMesh>();
    protected List<String> morphKeys = new ArrayList<String>();
    // Not using an ordered Map of some type for these paired lists, only
    // because of the atrocious J2SE support for Maps which "preserve" order
    // but do not "apply" an order (I.e. to just preserve the order in which
    // the items are added).
    protected TriMesh baseMorph;
    volatile private boolean dirty = true;
    protected boolean needExtrapolation;

    /**
     * @return the extrapolated vertexBuffer for the specified Morph, if one
     * was generated.  Otherwise, return the traditional vert buffer for it.
     */
    protected FloatBuffer getMorphVertBuffer(TriMesh m) {
        return (extrapolatedVertBuf == null
                || !extrapolatedVertBuf.containsKey(m))
                ? m.getVertexBuffer() : extrapolatedVertBuf.get(m);
    }

    /**
     * @return the extrapolated normBuffer for the specified Morph, if one
     * was generated.  Otherwise, return the traditional norm buffer for it.
     */
    protected FloatBuffer getMorphNormBuffer(TriMesh m) {
        return (extrapolatedNormBuf == null
                || !extrapolatedNormBuf.containsKey(m))
                ? m.getNormalBuffer() : extrapolatedNormBuf.get(m);
    }

    /** This one may be a reference to a remotely managed map */
    protected ListenableStringFloatMap morphInfluencesMap;

    /** This one is persisted with this MorphingTriMesh instance */
    protected ListenableStringFloatMap localMorphInfluencesMap;

    /**
     * @see #MorphingGeometry#getMorphInfluencesMap()
     */
    public ListenableStringFloatMap getMorphInfluencesMap() {
        return morphInfluencesMap;
    }

    /**
     * @see #MorphingGeometry#setMorphInfluencesMap(ListenableStringFloatMap)
     */
    public void setMorphInfluencesMap(ListenableStringFloatMap m) {
        if (localMorphInfluencesMap == m) return;
        if (localMorphInfluencesMap != null)
            localMorphInfluencesMap.removeListener(this);
        localMorphInfluencesMap = m;
        localMorphInfluencesMap.addListener(this, morphKeys);
        morphInfluencesMap = localMorphInfluencesMap;
    }

    /**
     * @see #MorphingGeometry#setSingleMorphInfluence(String, float)
     */
    public void setSingleMorphInfluence(String morphKey, float influence) {
        if (morphInfluencesMap == null)
            throw new IllegalStateException("No morphInfluences set");
        morphInfluencesMap.put(morphKey, influence);
    }

    /**
     * @see #MorphingGeometry#setMorphInfluences(
     *           Map<? extends String, ? extends Float>)
     */
    public void setMorphInfluences(Map<? extends String, ? extends Float> m) {
        if (morphInfluencesMap == null)
            throw new IllegalStateException("No morphInfluences set");
        morphInfluencesMap.putAll(m);
    }

    /**
     * @see #MorphingGeometry#delegateInfluences()
     */
    public boolean delegateInfluences() {
        ListenableStringFloatMap newInfluencesMap = null;
        Node ancestor = null;
        for (ancestor = getParent(); ancestor != null;
                ancestor = ancestor.getParent()) {
            logger.log(
                    Level.FINEST, "Trying anc ''{0}''...", ancestor.getName());
            if (!(ancestor instanceof MorphInfluencesMapProvider)) continue;
            newInfluencesMap =
                ((MorphInfluencesMapProvider) ancestor).getMorphInfluencesMap();
            if (newInfluencesMap != null) break;
        }
        if (newInfluencesMap == null) {
            logger.info("Failed to find a parent node to delegate to");
            return false;
        }
        if (morphInfluencesMap == newInfluencesMap) {
            logger.fine("The first MorphInfluencesMapProvider found has the "
                    + "same MorphInfluencesMap that we are already using");
            return false;
        }
        morphInfluencesMap = newInfluencesMap;
        morphInfluencesMap.addListener(this, morphKeys);
        logger.log(Level.INFO, "Delegating influences to ''{0}''", ancestor);
        return true;
    }

    /**
     * Constructor for internal use only.
     */
    public MorphingTriMesh() { }

    /**
     * Normal constructor.
     *
     * For local influence control, you should run <PRE><CODE>
     *     morphingTriMesh.addMorph(...);... // anytime before morph()ing
     *     morphingTriMesh.setMorphInfluencesMap(
     *             new ListenableStringFloatMap());
     *     morphingTriMesh.setMorphInfluences(...);
     *     morphingTriMesh.morph();
     * </CODE></PRE>
     * before attaching to a live scene.
     * For remote influence control, if you know there is an available
{'fraction_non_alphanumeric': 0.09376881396748947, 'fraction_numerical': 0.003160746538229982, 'mean_word_length': 3.031856796116505, 'pattern_counts': {'":': 0, '<': 38, '<?xml version=': 0, '>': 38, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7472234', 'n_tokens_mistral': 3888, 'n_tokens_neox': 3776, 'n_words': 991}