/** @file SketchSurface.java
*
* @author marco corvi
* @date feb 2013
*
* @brief TopoDroid 3d sketch: 3D surface
* --------------------------------------------------------
* Copyright This sowftare is distributed under GPL-3.0 or later
* See the file COPYING.
* --------------------------------------------------------
*/
package com.topodroid.DistoX;
// import java.io.StringWriter;
import java.io.PrintWriter;
import java.util.Collections;
import java.util.ArrayList;
import java.util.Random;
import java.util.Iterator;
import java.util.HashMap;
import java.util.Comparator;
import android.util.SparseArray;
import android.graphics.Paint;
import android.graphics.Canvas;
import android.graphics.Path;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.util.Log;
class SketchSurface extends SketchShot
{
SparseArray< SketchVertex > mVertices;
SparseArray< PointF > mCorners; // corners of forward surface: index-of-vertex, canvas-point
ArrayList< SketchSide > mSides;
ArrayList< SketchTriangle > mTriangles;
int maxkey_vtx;
// int maxkey_sds;
private SketchVertex mSelectedVertex;
// DEBUG
// void markTriangles( boolean hl )
// {
// for ( SketchTriangle t : mTriangles ) t.highlight = hl;
// }
/** borders are lists of sides with only one triangle,
* they are evaluated in computeBorders and are used donly by SketchDxf
* they are not saved in the th3 file
* they are displayed in the draw method
*/
ArrayList< SketchBorder > mBorders;
ArrayList< SketchVertex > mInsideVertices;
ArrayList< SketchTriangle > mInsideTriangles;
ArrayList< SketchTriangle > mOutsideTriangles;
SketchPainter mPainter;
// SketchTriangle mSelectedTriangle;
SketchSurface( String s1, String s2, SketchPainter painter )
{
super( s1, s2 );
mVertices = new SparseArray< SketchVertex >();
mCorners = new SparseArray< PointF >();
mTriangles = new ArrayList< SketchTriangle >();
mSides = new ArrayList<SketchSide>();
mBorders = new ArrayList< SketchBorder >();
mInsideTriangles = new ArrayList< SketchTriangle >();
mInsideVertices = new ArrayList< SketchVertex >();
mPainter = painter;
mSelectedVertex = null;
reset();
}
// void dump()
// {
// Log.v("DistoX", "Surface v " + mVertices.size() + " t " + mTriangles.size() );
// }
int findTrianglesInside( ArrayList<PointF> border )
{
mInsideTriangles.clear();
int ntin = 0;
for ( SketchTriangle t : mTriangles ) {
if ( t.isInside( border ) ) {
t.inside = true;
++ntin;
mInsideTriangles.add( t );
} else {
t.inside = false;
}
}
return ntin;
}
synchronized void refineAtCenters()
{
ArrayList< SketchTriangle > triangles = mTriangles;
mTriangles = new ArrayList< SketchTriangle >();
// ArrayList< SketchBorder > bborders = new ArrayList< SketchBorder >();
for ( SketchTriangle t : triangles ) {
int h = addVertex( t.center );
addTriangle( t.i, t.j, h );
addTriangle( t.j, t.k, h );
addTriangle( t.k, t.i, h );
}
}
synchronized void refineAtSides()
{
ArrayList< SketchTriangle > triangles = mTriangles;
mTriangles = new ArrayList< SketchTriangle >();
// ArrayList< SketchBorder > bborders = new ArrayList< SketchBorder >();
for ( SketchTriangle t : triangles ) {
Vector v1 = new Vector( (t.v2.x+t.v3.x)/2, (t.v2.y+t.v3.y)/2, (t.v2.z+t.v3.z)/2 );
Vector v2 = new Vector( (t.v3.x+t.v1.x)/2, (t.v3.y+t.v1.y)/2, (t.v3.z+t.v1.z)/2 );
Vector v3 = new Vector( (t.v1.x+t.v2.x)/2, (t.v1.y+t.v2.y)/2, (t.v1.z+t.v2.z)/2 );
int h1 = addVertex( v1 );
int h2 = addVertex( v2 );
int h3 = addVertex( v3 );
addTriangle( t.i, h3, h2 );
addTriangle( t.j, h1, h3 );
addTriangle( t.k, h2, h1 );
addTriangle(h1, h2, h3 );
}
}
synchronized void refineAtSelectedVertex( )
{
if ( mSelectedVertex == null ) {
// Log.v("DistoX", "refine at selected vertex: null selected veretex");
return;
}
ArrayList< SketchTriangle > triangles = mTriangles;
mTriangles = new ArrayList< SketchTriangle >();
for ( SketchTriangle t : triangles ) {
refineTriangleAtVertex( t, mSelectedVertex, 0.5f, 0.5f, true );
}
}
// project on (cos_clino*sin_azi, -cos_clino*cos_azimuth, -sin_clino)
private float computeProjections( SketchTriangle tri, Sketch3dInfo info )
{
SketchVertex v1 = mVertices.get( tri.i );
SketchVertex v2 = mVertices.get( tri.j );
SketchVertex v3 = mVertices.get( tri.k );
if ( v1 == null || v2 == null || v3 == null ) return -999;
// project on (cos_clino*sin_azi, -cos_clino*cos_azimuth, -sin_clino)
float z1 = info.worldToSceneOrigin( v1, tri.p1 );
float z2 = info.worldToSceneOrigin( v2, tri.p2 );
float z3 = info.worldToSceneOrigin( v3, tri.p3 );
return (z1+z2+z3)/3;
}
private void computeProjections3V( SketchTriangle tri, Vector v1, Vector v2, Vector v3, Sketch3dInfo info )
{
// project on (cos_clino*sin_azi, -cos_clino*cos_azimuth, -sin_clino)
PointF p = new PointF(0,0);
info.worldToSceneOrigin( v1, p ); tri.p1.x = p.x; tri.p1.y = p.y;
info.worldToSceneOrigin( v2, p ); tri.p2.x = p.x; tri.p2.y = p.y;
info.worldToSceneOrigin( v3, p ); tri.p3.x = p.x; tri.p3.y = p.y;
}
boolean refineTriangleAtVertex( SketchTriangle t, SketchVertex vv, float t2, float t3, boolean add )
{
Vector w1=null, w2=null, w3=null;
int i=t.i, j=t.j, k=t.k;
if ( vv == t.v1 ) {
w1 = t.v1; i = t.i;
w2 = t.v2; j = t.j;
w3 = t.v3; k = t.k;
} else if ( vv == t.v2 ) {
w1 = t.v2; i = t.j;
w2 = t.v3; j = t.k;
w3 = t.v1; k = t.i;
} else if ( vv == t.v3 ) {
w1 = t.v3; i = t.k;
w2 = t.v1; j = t.i;
w3 = t.v2; k = t.j;
} else {
/* not found vertex */
}
if ( w1 != null ) {
Vector v2 = new Vector( w1.x*t2+w2.x*(1-t2), w1.y*t2+w2.y*(1-t2), w1.z*t2+w2.z*(1-t2) );
Vector v3 = new Vector( w1.x*t3+w3.x*(1-t3), w1.y*t3+w3.y*(1-t3), w1.z*t3+w3.z*(1-t3) );
int h2 = addVertex( v2 );
int h3 = addVertex( v3 );
// PointF q2 = new PointF( p1.x*t2+p2.x*(1-t2), p1.y*t2+p2.y*(1-t2) );
// PointF q3 = new PointF( p1.x*t3+p3.x*(1-t3), p1.y*t3+p3.y*(1-t3) );
SketchTriangle tt = addTriangle( i, h2, h3 );
// computeProjections( tt, info );
if ( v2.distance( w3 ) > v3.distance( w2 ) ) {
tt = addTriangle( j, h3, h2 ); // h2 --- h3
// | .-' |
// j --- k
// computeProjections( tt, info );
tt = addTriangle( j, k, h3 );
// computeProjections( tt, info );
} else {
tt = addTriangle( k, h3, h2 ); // h2 --- h3
// | .-' |
// j --- k
// computeProjections( tt, info );
tt = addTriangle( j, k, h2 );
// computeProjections( tt, info );
}
return true;
} else if ( add ) {
SketchTriangle tt = addTriangle(t.i, t.j, t.k );
// computeProjections( tt, info );
}
return false;
}
boolean refineTriangleAtVertex( SketchTriangle t, SketchVertex vv, Vector v12, Vector v13, Sketch3dInfo info )
{
boolean first = true;
int i=t.i, j=t.j, k=t.k;
SketchVertex v2 = null, v3 = null;
if ( vv == t.v1 ) {
v2 = t.v2; v3 = t.v3;
first = ( v12.distance( v3 ) > v13.distance( v2 ) );
i = t.i; j = t.j; k = t.k;
} else if ( vv == t.v2 ) {
v2 = t.v3; v3 = t.v1;
first = ( v12.distance( v3 ) > v13.distance( v2 ) );
i = t.j; j = t.k; k = t.i;
} else if ( vv == t.v3 ) {
v2 = t.v1; v3 = t.v2;
first = ( v12.distance( v3 ) > v13.distance( v2 ) );
i = t.k; j = t.i; k = t.j;
} else {
/* not found vertex */
return false;
}
int h2 = addVertex( v12 );
int h3 = addVertex( v13 );
SketchVertex w2 = mVertices.get( h2 );
SketchVertex w3 = mVertices.get( h3 );
synchronized( mTriangles ) {
addTriangle( i, h2, h3, vv, w2, w3, info );
if ( first ) {
addTriangle( j, h3, h2, v2, w3, w2, info );
addTriangle( j, k, h3, v2, v3, w3, info );
} else {
addTriangle( k, h3, h2, v3, w3, w2, info );
addTriangle( j, k, h2, v2, v3, w2, info );
}
mTriangles.remove( t );
}
return true;
}
// synchronized boolean removeTriangle( SketchTriangle t )
// {
// return mTriangles.remove( t );
// }
SketchBorder getBorderAt( String name, DistoXNum num )
{
Vector s0 = null;
if ( st1.equals( name ) || st2.equals( name ) ) {
NumStation st = num.getStation( name );
s0 = new Vector( st.e, st.s, st.v );
}
if ( s0 != null ) {
float d0 = 10000f;
SketchBorder b0 = null;
for ( SketchBorder border : mBorders ) {
float d = s0.distance( border.getCenter(this) );
if ( d < d0 ) {
d0 = d;
b0 = border;
}
}
return b0;
}
return null;
}
SketchVertex getSelectedVertex( ) { return mSelectedVertex; }
void setSelectedVertex( SketchVertex v ) { mSelectedVertex = v; }
private void reset()
{
mVertices.clear();
mTriangles.clear();
mSides.clear();
mBorders.clear();
maxkey_vtx = -1;
// maxkey_sds = -1;
}
void clearHighlight()
{
for ( SketchTriangle t : mTriangles ) t.highlight = false;
}
/** n01 + ------- + n11
* | `-._ |
* | `-. |
* n02 + - - - - + n12
* | `-. |
*/
// synchronized void makeExtrude( ArrayList<Vector> pts, ArrayList< Vector > border3d )
// {
// // TODO EXTRUDE
// }
class SketchSideComparator implements Comparator< SketchSide >
{
@Override
public int compare( SketchSide lhs, SketchSide rhs )
{
return ( lhs.length < rhs.length )? 1 :
( lhs.length == rhs.length )? 0 : -1;
}
}
int refineToMaxSide( float max_size )
{
if ( mSides.size() == 0 ) return 0;
for ( SketchTriangle t : mTriangles ) t.splitted = false;
// boolean repeat = true;
// int cnt = 0;
int split = 0; // total number of splits
SketchSideComparator comp = new SketchSideComparator();
// while ( repeat && cnt < 1 )
{
// ++ cnt;
// repeat = false;
computeSidesWithLength();
// sort sides by the length
Collections.sort( mSides, comp );
float len = mSides.get(0).length;
for ( SketchSide s : mSides ) {
// if ( s.length > len ) {
// Log.v("DistoX", "not decresing side length " + len + " " + s.length );
// }
len = s.length;
}
// ArrayList< SketchTriangles > triangles = mTriangles;
// Log.v( "DistoX", "repeat " + cnt + " sides " + mSides.size() );
// float size = 0;
synchronized( mTriangles ) {
for ( SketchSide s : mSides ) {
int k1 = s.v1;
int k2 = s.v2;
SketchVertex v1 = mVertices.get( s.v1 );
SketchVertex v2 = mVertices.get( s.v2 );
float d = v1.distance( v2 );
// if ( d > size ) size = d;
if ( d > max_size ) {
SketchTriangle t1 = s.t1;
SketchTriangle t2 = s.t2;
if ( t1 != null && t2 != null && ! t1.splitted && ! t2.splitted ) {
int k13 = -1, k23 = -1;
boolean f1 = false, f2 = false;
if ( k1 == t1.i && k2 == t1.j ) {
k13 = t1.k;
f1 = true;
} else if ( k1 == t1.j && k2 == t1.i ) {
k13 = t1.k;
f1 = false;
} else if ( k1 == t1.j && k2 == t1.k ) {
k13 = t1.i;
f1 = true;
} else if ( k1 == t1.k && k2 == t1.j ) {
k13 = t1.i;
f1 = false;
} else if ( k1 == t1.k && k2 == t1.i ) {
k13 = t1.j;
f1 = true;
} else if ( k1 == t1.i && k2 == t1.k ) {
k13 = t1.j;
f1 = false;
}
if ( k1 == t2.i && k2 == t2.j ) {
k23 = t2.k;
f2 = true;
} else if ( k1 == t2.j && k2 == t2.i ) {
k23 = t2.k;
f2 = false;
} else if ( k1 == t2.j && k2 == t2.k ) {
k23 = t2.i;
f2 = true;
} else if ( k1 == t2.k && k2 == t2.j ) {
k23 = t2.i;
f2 = false;
} else if ( k1 == t2.k && k2 == t2.i ) {
k23 = t2.j;
f2 = true;
} else if ( k1 == t2.i && k2 == t2.k ) {
k23 = t2.j;
f2 = false;
}
if ( k13 >= 0 && k23 >= 0 ) { // split side
Vector w = new Vector( (v1.x+v2.x)/2, (v1.y+v2.y)/2, (v1.z+v2.z)/2 );
// make triangles
int kw = addVertex( w );
if ( f1 ) { // t1: v1 --(w)--> v2 ---> v13 ==> v1-w-v13 and w-v2-v13
addTriangle( kw, k13, k1 );
addTriangle( kw, k2, k13 );
} else { // t1: v2 --> v1 --> v13
addTriangle( kw, k13, k2 );
addTriangle( kw, k1, k13 );
}
if ( f2 ) {
addTriangle( kw, k23, k1 );
addTriangle( kw, k2, k23 );
} else {
addTriangle( kw, k23, k2 );
addTriangle( kw, k1, k23 );
}
t1.splitted = true;
t2.splitted = true;
// repeat = true;
++split;
}
}
}
}
// Log.v("DistoX", "split " + split + " size " + size );
}
}
ArrayList< SketchTriangle > triangles = new ArrayList< SketchTriangle >();
for ( SketchTriangle t : mTriangles ) {
if ( ! t.splitted ) triangles.add( t );
}
synchronized( mTriangles ) {
mTriangles = triangles;
}
return split;
}
synchronized void makeCut( )
{
// remove inside triangles
// ArrayList< SketchVertex > outerVertices = new ArrayList<SketchVertex>();
// int ntin = 0;
// for ( SketchTriangle t : mTriangles ) {
// if ( ! t.inside ) {
// if ( ! outerVertices.contains( t.v1 ) ) outerVertices.add( t.v1 );
// if ( ! outerVertices.contains( t.v2 ) ) outerVertices.add( t.v2 );
// if ( ! outerVertices.contains( t.v3 ) ) outerVertices.add( t.v3 );
// } else {
// ++ ntin;
// }
// }
// Log.v( "DistoX", "make cut: in triangles " + ntin + " / " + mTriangles.size() );
// Log.v( "DistoX", "make cut: outer vertices " + outerVertices.size() + " / " + mVertices.size() );
ArrayList<SketchTriangle> triangles = mTriangles;
mVertices = new SparseArray< SketchVertex >();
mTriangles = new ArrayList< SketchTriangle >();
synchronized( mTriangles ) {
for ( SketchTriangle t : triangles ) {
if ( ! t.inside ) {
int ka = addVertex( t.v1 );
int kb = addVertex( t.v2 );
int kc = addVertex( t.v3 );
addTriangle( ka, kb, kc );
}
}
}
}
// private float determinant( PointF p0, PointF p1, PointF p2 )
// {
// return p1.x*p2.y - p1.y*p2.x + p0.x*p1.y - p0.y*p1.x + p2.x*p0.y - p2.y*p0.x;
// }
// --------------------------------------------------------
// borders
private SketchSide getSide( int v1, int v2 )
{
for ( SketchSide side : mSides ) {
if ( side.v1 == v1 && side.v2 == v2 ) return side;
if ( side.v1 == v2 && side.v2 == v1 ) return side;
}
return null;
}
/** called by computeSides() below
*/
private void checkSide( SketchTriangle tri, int i, int j, Vector v1, Vector v2 )
{
SketchSide side = getSide( i, j );
if ( side == null ) {
int idx = mSides.size();
side = new SketchSide( this, idx, i, j );
if ( v1 != null && v2 != null ) side.length = v1.distance( v2 );
mSides.add( side );
mSides.get( idx ).t1 = tri;
} else {
if ( side.t1 == null ) {
Log.e("DistoX", "null triangle-1 side");
side.t1 = tri;
} else if ( side.t2 == null ) {
side.t2 = tri;
} else {
Log.e("DistoX", "multi T side " + side.t1.i + " " + side.t1.j + " " + side.t1.k + " " + side.t2.i + " " + side.t2.j + " " + side.t2.k + " " + tri.i + " " + tri.j + " " + tri.k );
}
}
}
/** called by ComputeBorders, makeOuterTriangles, and makeExtrude
*/
private void computeSides( )
{
mSides.clear();
for ( SketchTriangle t : mTriangles ) {
if ( ! t.splitted ) {
checkSide( t, t.i, t.j, null, null );
checkSide( t, t.j, t.k, null, null );
checkSide( t, t.k, t.i, null, null );
}
}
}
private void computeSidesWithLength()
{
mSides.clear();
for ( SketchTriangle t : mTriangles ) {
if ( ! t.splitted ) {
checkSide( t, t.i, t.j, t.v1, t.v2 );
checkSide( t, t.j, t.k, t.v2, t.v3 );
checkSide( t, t.k, t.i, t.v3, t.v1 );
}
}
}
/** called by SkecthModel
* compute the borders of a surface.
* a border is a chain of sides that have only one triangle
*/
void computeBorders()
{
computeSides( );
ArrayList< SketchSide > tmp = new ArrayList<SketchSide>();
for ( SketchSide s : mSides ) {
if ( s.t2 == null ) { tmp.add( s ); }
}
mBorders.clear();
// Log.v("DistoX", "compute borders ns " + tmp.size() );
while ( tmp.size() > 0 ) {
SketchBorder brd = new SketchBorder();
SketchSide s1 = tmp.get( 0 );
tmp.remove( s1 );
brd.add( s1 );
int v1 = s1.v1;
int v2 = s1.v2;
while ( v2 != v1 ) {
boolean found = false;
for ( SketchSide s2 : tmp ) {
if ( s2.v1 == v2 || s2.v2 == v2 ) {
tmp.remove( s2 );
brd.add( s2 );
v2 = ( s2.v1 == v2 )? s2.v2 : s2.v1; // the other vertex of s2 different from v2
found = true;
break;
}
}
if ( ! found ) {
for (SketchSide s2 : tmp ) {
if ( s2.v1 == v1 || s2.v2 == v1 ) {
tmp.remove( s2 );
brd.sides.add( 0, s2 );
v1 = ( s2.v1 == v1 )? s2.v2 : s2.v1;
found = true;
break;
}
}
}
if ( ! found ) {
// Log.v("DistoX", "broken border length " + brd.sides.size() );
break;
}
}
// StringWriter sw = new StringWriter();
// PrintWriter pw = new PrintWriter( sw );
// for ( SketchSide s : brd.sides ) {
// pw.format("%d-%d ", s.v1, s.v2 );
// }
// Log.v("DistoX", sw.getBuffer().toString() );
mBorders.add( brd );
}
// Log.v("DistoX", "compute border surface nr. border " + mBorders.size() );
}
// --------------------------------------------------------
// VERTICES
/** add a vertex with a given index ( for loadTh3 )
* @param n vertex index
* @reurn the new-vertex index
*/
int addVertex( int n, float x, float y, float z ) {
SketchVertex v = new SketchVertex( this, n, x, y, z );
mVertices.put( n, v );
if ( n > maxkey_vtx ) maxkey_vtx = n;
return n;
}
/** add a vertex giving the 3D vector
* @param v input 3D vector
*/
private int addVertex( Vector v ) { return addVertex( v.x, v.y, v.z ); }
/** add a vertex given X,Y,Z (or return an already existing close vertex)
* @return the vertex index
*/
private int addVertex( float x, float y, float z )
{
int size = mVertices.size();
for ( int k = 0; k<size; ++ k ) {
SketchVertex v = mVertices.valueAt( k );
if ( Math.abs( x - v.x ) < 0.1 && Math.abs( y - v.y ) < 0.1 && Math.abs( z - v.z ) < 0.1 ) {
return v.index;
}
}
++ maxkey_vtx;
SketchVertex v = new SketchVertex( this, maxkey_vtx, x, y, z );
mVertices.put( maxkey_vtx, v );
return maxkey_vtx;
}
/** get a vertex given the index
* @param index vertex index
* @return the vertex
* used by SketchBorder
*/
SketchVertex getVertex( int index )
{
return mVertices.get( index );
}
// --------------------------------------------------------
// triangles
// final float r2_2 = TDMath.sqrt( 0.5f ); // sqrt(2)/2
/**
* @return the new vertex index
*/
// private int interpolateVertices( SketchVertex v1, SketchVertex v2, float a, float r, Sketch3dInfo info )
// {
// float b = 1 - a;
// // float r = 1/TDMath.sqrt( a*a + b*b );
// Vector v = new Vector( a*v1.x+b*v2.x - info.station1.e,
// a*v1.y+b*v2.y - info.station1.s,
// a*v1.z+b*v2.z - info.station1.v );
// float nv = (v.x*info.sin_alpha + v.y*info.cos_alpha)*info.cos_gamma + v.z*info.sin_gamma;
// Vector u = new Vector( nv*info.sin_alpha*info.cos_gamma, nv*info.cos_alpha*info.cos_gamma, nv*info.sin_gamma );
// return addVertex( info.station1.e + u.x + r*(v.x - u.x),
// info.station1.s + u.y + r*(v.y - u.y),
// info.station1.v + u.z + r*(v.z - u.z) );
// }
// from loadTh3 Therion
// void addSide( int index, int v1, int v2 )
// {
// SketchVertex w1 = mVertices.get( v1 );
// SketchVertex w2 = mVertices.get( v2 );
// if ( w1 == null || w2 == null ) {
// Log.e("DistoX", "ERROR side without vertex: " + v1 + " " + v2 );
// }
// mSides.put( index, new SketchSide( this, index, v1, v2 ) );
// if ( index > maxkey_sds ) maxkey_sds = index;
// }
// SketchSide getSide( int i, int j )
// {
// for ( SketchSide s : mSides.values() ) {
// if ( s.hasVertices( i, j ) ) return s;
// }
// ++ maxkey_sds;
// SketchSide s1 = new SketchSide( this, maxkey_sds, i, j );
// mSides.put( maxkey_sds, s1 );
// return s1;
// }
// SketchTriangle addTriangle3V( Vector v1, Vector v2, Vector v3 )
// {
// int ka = addVertex( t.mA );
// int kb = addVertex( t.mB );
// int kc = addVertex( t.mC );
// addTriangle( ka, kb, kc );
// }
// add triangle
SketchTriangle addTriangle( int i, int j, int k )
{
return addTriangle( i, j, k, mVertices.get(i), mVertices.get(j), mVertices.get(k) );
}
private SketchTriangle addTriangle( int i, int j, int k, SketchVertex v1, SketchVertex v2, SketchVertex v3 )
{
SketchTriangle t = null;
if ( i != j && j != k && k != i ) {
if ( v1 == null || v2 == null || v3 == null ) {
TDLog.Error("ERROR add triangle with a null vertex. Vertex indices " + i + " " + j + " " + k );
} else {
t = new SketchTriangle( this, i, j, k, v1, v2, v3 );
mTriangles.add( t );
}
}
return t;
}
private SketchTriangle addTriangle( int i, int j, int k, SketchVertex v1, SketchVertex v2, SketchVertex v3, Sketch3dInfo info )
{
SketchTriangle t = null;
if ( i != j && j != k && k != i ) {
if ( v1 == null || v2 == null || v3 == null ) {
TDLog.Error("ERROR add triangle with a null vertex. Vertex indices " + i + " " + j + " " + k );
} else {
t = new SketchTriangle( this, i, j, k, v1, v2, v3 );
computeProjections3V( t, v1, v2, v3, info );
mTriangles.add( t );
}
}
return t;
}
// private float equilaterity( int i, int j, int k )
// {
// SketchVertex v1 = mVertices.get( i );
// // SketchVertex v2 = mVertices.get( j );
// SketchVertex v3 = mVertices.get( k );
// return v1.distance( v3 );
// }
// -----------------------------------------------------------
/** The angle around the unit vector of the shot
* +-----------> unit
* / \
* dir2/ \ dir1
* v v
*
* dir2 = unit ^ dir1 The angles are in the (dir1,dir2) plane:
*
* +-------> dir1
* |\
* | \ X
* dir2 v
* sin( alpha ) = X * dir1
* cos( alpha ) = X * dir2
*/
private int computeAngles( ArrayList<Vector> pts, Vector base, Vector unit, Vector dir1, Vector dir2, float[] angle )
{
int ns = pts.size();
// Vector retn[] = new Vector[ns];
Vector retn = new Vector();
for ( int n=0; n<ns; ++n ) {
Vector v = pts.get(n);
retn.x = v.x - base.x;
retn.y = v.y - base.y;
retn.z = v.z - base.z;
float p = retn.dot( unit );
retn.x -= p * unit.x;
retn.y -= p * unit.y;
retn.z -= p * unit.z;
retn.normalize();
angle[n] = TDMath.atan2( retn.dot(dir1), retn.dot(dir2) );
}
float a = angle[0] - angle[ns-1];
for ( int n=1; n<ns; ++n ) {
float da = angle[n] - angle[n-1];
a += ( da > 1.57 )? da - 3.14 : ( da < -1.57 ) ? da + 3.14 : da;
}
// Log.v("DistoX", "angle around " + a);
// if (a > 0.0f) { // revert
// int n0 = 0;
// int n1 = ns - 1;
// while ( n0 < n1 ) {
// float aa = angle[n0]; angle[n0] = angle[n1]; angle[n1] = aa;
// Vector v0 = pts.get( n0 );
// Vector v1 = pts.get( n1 );
// pts.set( n0, v1 );
// pts.set( n1, v0 );
// ++ n0;
// -- n1;
// }
// }
// return 1;
return ( a < 0.0f )? ns - 1 : 1;
}
private void addTriangles(int k00, int k10, int k11, int nt)
{
if ( nt <= 1 ) {
addTriangle( k00, k10, k11 );
} else {
SketchVertex v00 = mVertices.get( k00 );
SketchVertex v10 = mVertices.get( k10 );
SketchVertex v11 = mVertices.get( k11 );
Vector dv0 = new Vector( (v10.x - v00.x )/nt, (v10.y - v00.y)/nt, (v10.z - v00.z)/nt );
Vector dv1 = new Vector( (v11.x - v00.x )/nt, (v11.y - v00.y)/nt, (v11.z - v00.z)/nt );
for ( int n = nt-1; n > 0; --n ) {
int k0 = addVertex( new Vector( v00.x + dv0.x * n, v00.y + dv0.y * n, v00.z + dv0.z * n ) );
int k1 = addVertex( new Vector( v00.x + dv1.x * n, v00.y + dv1.y * n, v00.z + dv1.z * n ) );
addTriangle( k0, k10, k11 );
addTriangle( k0, k11, k1 );
k10 = k0;
k11 = k1;
}
addTriangle( k00, k10, k11 );
}
}
/** check if two arc-segments on the (unit) sphere intersect
* (p1,p2) first segment
* (q1,q2) second segment
* @param p1 first unit vector of the first pair
* @param p2 second unit vector of the first pair
* @param q1 first unit vector of the second pair
* @param q2
*/
private boolean arcIntersect( Vector p1, Vector p2, Vector q1, Vector q2 )
{
Vector np = p1.cross( p2 ); // "normal" to the plane (p1,p2)
Vector nq = q1.cross( q2 ); // "normal" to the plane (q1,q2)
Vector nn = np.cross( nq );
nn.normalize(); // intersection of the planes (p1,p2) (q1,q2)
if ( p1.cross( nn ).dot( p1.cross( p2 ) ) < 0 ) nn.reverse();
if ( p1.cross( nn ).dot( nn.cross( p2 ) ) < 0 ) return false;
if ( q1.cross( nn ).dot( q1.cross( q2 ) ) < 0 ) return false;
if ( q1.cross( nn ).dot( nn.cross( q2 ) ) < 0 ) return false;
return true;
}
/** a pair of indices
*/
private class VPair
{
int v1, v2;
VPair( int x1, int x2 )
{
v1 = x1;
v2 = x2;
}
};
/** check if the array of index-pairs contains a given pair
* @param n1 first index of the pair
* @param n2 second index of the pair
* @param vts array of pairs
* @param nv number of pairs in the array
*/
private boolean hasPair( int n1, int n2, VPair[] vts, int nv )
{
for ( int v=0; v<nv; ++v ) {
if ( ( vts[v].v1 == n1 && vts[v].v2 == n2 )
|| ( vts[v].v1 == n2 && vts[v].v2 == n1 ) ) return true;
}
return false;
}
private final static float MAX_DIST = 10f; // FIXME > 2*PI+eps
private final static float MAX_DIST2 = 5f; // MAX_DIST / 2
/**
* @param ss set of cross-sections at the station
* @param info
* @param splays splays at the station
* @param center station 3D coords
*/
// void makeJoinTriangles( SketchSectionSet ss, Sketch3dInfo info, ArrayList< SketchFixedPath > splays, Vector center )
// {
// int ms = ss.size();
// if ( ms < 2 ) return;
// int NP = 0;
// for ( int k=0; k<ms; ++k ) {
// int np0 = ss.getSection(k).mLine.points.size();
// NP += np0;
// // Log.v("DistoX", "Sections " + k + " points " + np0 );
// }
//
// int [] off = new int[ ms+1 ];
// off[0] = 0;
// int np = 0;
// Vector[] pts = new Vector[ NP ];
// int nv = 0;
// int NV = 2*NP + 3*(ms-2);
// VPair[] vts = new VPair[ NV ]; // vertex pairs connected by a side
// for ( int k=0; k<ms; ++k ) {
// SketchSection s = ss.getSection( k );
// ArrayList<Vector> pts0 = s.mLine.points;
// int n0 = pts0.size();
// for ( int n=0; n<n0; ++n ) {
// Vector v0 = pts0.get(n);
// addVertex( np, v0.x, v0.y, v0.z ); // prepare the surface vertices
//
// v0.sub( center );
// pts[ np ] = v0.getUnitVector();
// // Log.v("DistoX", "P" + np + ": " + pts[np].x + " " + pts[np].y + " " + pts[np].z );
// ++ np;
// }
// off[ k + 1 ] = np;
// }
// // Log.v("DistoX", "Nr. sections " + ms + " NP " + NP + " np " + np + " nv " + nv );
// float[] dist = new float[ np * np ];
// for ( int m=0; m<ms; ++m ) {
// for ( int k = off[m]; k < off[m+1]; ++k ) {
// for ( int h = off[m]; h < off[m+1]; ++h ) {
// dist[ k*np + h ] = MAX_DIST;
// }
// }
// for ( int n=m+1; n<ms; ++n ) {
// for ( int k = off[m]; k < off[m+1]; ++k ) {
// int nk = off[m+1] - off[m];
// for ( int h = off[n]; h < off[n+1]; ++h ) {
// int nh = off[n+1] - off[n];
// boolean ok = true;
// for ( int m1=0; ok && m1<ms; ++m1 ) {
// int k1 = off[m1+1] - 1;
// for ( int h1=off[m1]; h1 < off[m1+1]; ++h1 ) {
// if ( h != h1 && h != k1 && k != h1 && h != k1 &&
// arcIntersect( pts[k], pts[h], pts[k1], pts[h1] ) ) {
// ok = false;
// break;
// }
// k1 = h1;
// }
// }
// // if ( ok && arcIntersect( pts[k], pts[h],
// // pts[off[m] + (k-off[m]+nk/4)%nk], pts[off[m] + (k-off[m]+3*nk/4)%nk] ) ) {
// // ok = false;
// // }
// // if ( ok && arcIntersect( pts[k], pts[h],
// // pts[off[n] + (h-off[n]+nh/4)%nh], pts[off[n] + (h-off[n]+3*nh/4)%nh] ) ) {
// // ok = false;
// // }
// if ( ok ) {
// dist[ k*np + h ] = (float) Vector.arc_distance( pts[k], pts[h] );
// dist[ h*np + k ] = dist[ k*np + h ]; // not necessary
// // Log.v("DistoX", "dist. " + k + "-" + h + " " + dist[k*np+h ] );
// } else {
// dist[ k*np + h ] = MAX_DIST;
// dist[ h*np + k ] = MAX_DIST;
// }
// }
// }
// }
// }
// for ( int m0=0; m0<ms; ++m0 ) {
// for ( int h0 = off[m0]; h0 < off[m0+1]; ++h0 ) {
// int k0 = (h0>off[m0])? h0 - 1 : off[m0+1] - 1;
// // Vector p1 = pts[ k0 ];
// // Vector p2 = pts[ h0 ];
// // for ( int m=0; m<ms; ++m ) {
// // for ( int n=m+1; n<ms; ++n ) {
// // for ( int k = off[m]; k < off[m+1]; ++k ) {
// // if ( k == k0 ) continue;
// // for ( int h = off[n]; h < off[n+1]; ++h ) { // NOTE h > k
// // if ( h == h0 ) continue;
// // if ( dist[ k*np+h ] < MAX_DIST2 && arcIntersect( p1, p2, pts[k], pts[h] ) ) {
// // // Log.v("DistoX", "arc-intersect " + k0 + "-" + h0 + " " + k + "-" + h );
// // dist[ k*np + h ] = MAX_DIST;
// // dist[ h*np + k ] = MAX_DIST;
// // }
// // }
// // }
// // }
// // }
// vts[ nv ] = new VPair( k0, h0 );
// ++nv;
// }
// }
// while ( nv < NV ) {
// float dmin = MAX_DIST2;
// int kmin = -1;
// int hmin = -1;
// for ( int m=0; m<ms; ++m ) {
// for ( int n=m+1; n<ms; ++n ) {
// for ( int k = off[m]; k < off[m+1]; ++k ) {
// for ( int h = off[n]; h < off[n+1]; ++h ) {
// if ( dist[ k*np + h ] < dmin ) {
// dmin = dist[ k*np + h ];
// kmin = k;
// hmin = h;
// }
// }
// }
// }
// }
// if ( kmin < 0 ) break; // NOTE hmin > kmin
// Vector p1 = pts[ kmin ];
// Vector p2 = pts[ hmin ];
// for ( int m=0; m<ms; ++m ) {
// for ( int n=m+1; n<ms; ++n ) {
// for ( int k = off[m]; k < off[m+1]; ++k ) {
// if ( k == kmin ) continue;
// for ( int h = off[n]; h < off[n+1]; ++h ) { // NOTE h > k
// if ( h == hmin ) continue;
// if ( dist[ k*np+h ] < MAX_DIST2 && arcIntersect( p1, p2, pts[k], pts[h] ) ) {
// // Log.v("DistoX", "arc-intersect " + kmin + "-" + hmin + " " + k + "-" + h );
// dist[ k*np + h ] = MAX_DIST;
// dist[ h*np + k ] = MAX_DIST;
// }
// }
// }
// }
// }
// dist[ kmin*np + hmin ] = MAX_DIST;
// dist[ hmin*np + kmin ] = MAX_DIST;
// // Log.v("DistoX", "pair " + nv + ": " + kmin + " " + hmin );
// vts[ nv ] = new VPair( kmin, hmin );
// ++nv;
// }
// // Log.v("DistoX", "Nr. vertices " + np + " Nr. sides " + nv + " Left over:");
// // for ( int k =0; k<off[ms]; ++k ) { // DEBUG log
// // for ( int h =0; h<off[ms]; ++h ) {
// // if ( dist[k*np+h] < MAX_DIST2 ) Log.v("DistoX", k + "-" + h + " " + dist[k*np+h] );
// // }
// // }
// // now make triangles
// int nt = 0;
// for ( int n1 = 0; n1 < NP; ++n1 ) {
// for ( int n2 = n1+1; n2 < NP; ++n2 ) {
// if ( hasPair( n1, n2, vts, nv ) ) {
// for ( int n3 = n2+1; n3 < NP; ++n3 ) {
// if ( hasPair( n2, n3, vts, nv ) && hasPair( n3, n1, vts, nv ) ) {
// if ( Vector.triple_product( pts[n1], pts[n2], pts[n3] ) > 0 ) {
// addTriangle( n1, n2, n3 );
// } else {
// addTriangle( n1, n3, n2 );
// }
// ++ nt;
// }
// }
// }
// }
// }
// // Log.v("DistoX", "Nr. triangles " + nt );
// }
void makeTriangles( Sketch3dInfo info, ConvexHull hull )
{
Vector unit = info.shotUnit(); // shot unit-vector
ArrayList< Triangle > tri = hull.mTri;
ArrayList< Vector > pts = new ArrayList<Vector>();
for ( Triangle t : tri ) {
// Triangle has vertces mA mB mC and outgoing normal mN
int ka = addVertex( t.mA );
int kb = addVertex( t.mB );
int kc = addVertex( t.mC );
addTriangle( ka, kb, kc );
}
computeBorders();
}
// void makeTriangles( SketchSectionSet ss, Sketch3dInfo info, ArrayList< SketchFixedPath > splays1,
// ArrayList< SketchFixedPath > splays2 )
// {
// int ms = ss.size();
// if ( ms < 2 ) return;
// int type = ss.mType;
// Vector unit = info.shotUnit(); // shot unit-vector
// Vector dir1 = new Vector(0,0,0); // first orthogonal unit-vector
// if ( Math.abs( unit.x ) > Math.abs( unit.y ) ) {
// if ( Math.abs( unit.y ) > Math.abs( unit.z ) ) { // x > y > z
// dir1.x = unit.y;
// dir1.y = -unit.x;
// } else { // x > z > y or z > x > y
// dir1.x = -unit.z;
// dir1.z = unit.x;
// }
// } else {
// if ( Math.abs( unit.x ) > Math.abs( unit.z ) ) { // y > x > z
// dir1.x = unit.y;
// dir1.y = -unit.x;
// } else { // y > z > x or z > y > x
// dir1.y = unit.z;
// dir1.z = -unit.y;
// }
// }
// dir1.normalize();
// Vector dir2 = unit.cross(dir1); // second orthogonal unit-vector
// Vector v0, v1, v2;
// SketchSection s0 = ss.getSection(0);
// ArrayList<Vector> pts0 = s0.mLine.points;
// int ms0 = pts0.size();
// float angle0[] = new float[ms0];
// int dm0 = computeAngles( pts0, s0.mBasePoint, unit, dir1, dir2, angle0 );
// int m0start = 0;
// for ( int m0=0; m0<ms0; ++m0 ) if ( angle0[m0] < angle0[m0start] ) m0start = m0;
// // int dm0 = ( angle0[m0start + 1 + ms0/5] < 0 )? 1 : ms0 - 1;
// for (int m=1; m<ms; ++m ) {
// SketchSection s1 = ss.getSection(m);
// ArrayList<Vector> pts1 = s1.mLine.points;
// int ns1 = pts1.size();
// float angle1[] = new float[ns1];
// int dn1 = computeAngles( pts1, s1.mBasePoint, unit, dir1, dir2, angle1 );
// int n1start = 0;
// for ( int n1=0; n1<ns1; ++n1 ) if ( angle1[n1] < angle1[n1start] ) n1start = n1;
// // int dn1 = ( angle1[n1start + 1 + ns1/5] < 0 )? 1 : ns1 - 1;
// float len = s1.mBasePoint.minus( s0.mBasePoint ).Length();
// int nt = 1 + (int)(len / 0.5f); // number of triangle per side
// // Log.v( "DistoX", "nt " + nt + " len " + len + " m0 " + m0start + "/" + ms0 + " n1 " + n1start + "/" + ns1 );
// int m00 = m0start;
// int n10 = n1start;
// int m01 = (m0start + dm0)%ms0;
// int n11 = (n1start + dn1)%ns1;
// boolean invert = false; // (ms0 != 1) ^ (ns1 != 1);
// int k00 = addVertex( pts0.get( m0start ) ); // k00 + + k10
// int k01 = addVertex( pts0.get( m01 ) ); // | |
// int k10 = addVertex( pts1.get( n1start ) ); // k01 + + k11
// int k11 = addVertex( pts1.get( n11 ) ); // | |
// int k0start = k00;
// int k1start = k10;
// boolean do0 = true;
// boolean do1 = true;
// while ( do0 || do1 ) {
// if ( ! do0 ) {
// if ( invert ) {
// addTriangles(k00, k10, k11, nt);
// } else {
// addTriangles(k00, k11, k10, nt);
// }
// k10 = k11;
// n10 = n11;
// if ( k10 == k1start ) break;
// n11 = ( n11 + dn1 ) % ns1;
// k11 = addVertex( pts1.get( n11 ) );
// } else if ( ! do1 ) {
// if ( invert ) {
// addTriangles(k10, k01, k00, nt);
// } else {
// addTriangles(k10, k00, k01, nt);
// }
// k00 = k01;
// m00 = m01;
// if ( k00 == k0start ) break;
// m01 = ( m01 + dm0 ) % ms0;
// k01 = addVertex( pts0.get( m01 ) );
// } else {
// if ( angle1[n11] < angle0[m01] ) {
// if ( invert ) {
// addTriangles(k00, k10, k11, nt);
// } else {
// addTriangles(k00, k11, k10, nt); // k00 +------+ k10
// } // | |
// k10 = k11; // + + k11
// n10 = n11;
// if ( k10 == k1start ) {
// do1 = false;
// } else {
// n11 = ( n11 + dn1 ) % ns1;
// k11 = addVertex( pts1.get( n11 ) );
// }
// } else {
// if ( invert ) {
// addTriangles(k10, k01, k00, nt);
// } else {
// addTriangles(k10, k00, k01, nt); // k00 +------+ k10
// }
// k00 = k01; // k01 + +
// m00 = m01;
// if ( k00 == k0start ) {
// do0 = false;
// } else {
// m01 = ( m01 + dm0 ) % ms0; // | |
// k01 = addVertex( pts0.get( m01 ) );
// }
// }
// }
// }
// s0 = s1;
// pts0 = pts1;
// ms0 = ns1;
// dm0 = dn1;
// m0start = n1start;
// angle0 = angle1;
// }
// if ( TopoDroidApp.mSketchUsesSplays ) { // take into account splays
// for ( SketchFixedPath s : splays1 ) {
// // s.mLine.points is an array of Vector
// // if ( s.mLine.points.size() != 2 ) Log.v("DistoX", "splays at 1 pts " + s.mLine.points.size() );
// v1 = s.mLine.points.get( 0 );
// v2 = s.mLine.points.get( 1 );
// for ( SketchTriangle tri : mTriangles ) {
// v0 = tri.intersection( v1, v2 );
// if ( v0 != null ) {
// // Log.v("DistoX", "splay1 intersect at " + v0.x + " " + v0.y + " " + v0.z );
// tri.shiftVertices( v0 );
// }
// }
// }
// for ( SketchFixedPath s : splays2 ) {
// // if ( s.mLine.points.size() != 2 ) Log.v("DistoX", "splays at 2 pts " + s.mLine.points.size() );
// v1 = s.mLine.points.get( 0 );
// v2 = s.mLine.points.get( 1 );
// for ( SketchTriangle tri : mTriangles ) {
// v0 = tri.intersection( v1, v2 );
// if ( v0 != null ) {
// // Log.v("DistoX", "splay2 intersect at " + v0.x + " " + v0.y + " " + v0.z );
// tri.shiftVertices( v0 );
// }
// }
// }
// }
// computeBorders();
// }
/** make triangles. the triangles border are oriented:
*
* n1 * -- (k-1)<--(k) -- ... -- *
* | /|
* | / |
* v / ^
* | / |
* |/ |
* n2 * -- (k-1)-->(k) -- ... -- *
*/
void makeTriangles( ArrayList<Vector> pts, int npts )
{
int nn = pts.size();
int[] idx = new int[ nn ];
int k = 0;
for ( Vector p : pts ) {
idx[k] = addVertex( p );
++k;
}
nn = nn/npts - 1;
for ( int n = 0; n < nn; ++n ) {
int n1 = n * npts;
int n2 = (n+1) * npts;
k = 0;
addTriangle( idx[n1+npts-1], idx[n2+npts-1], idx[n1+0] );
addTriangle( idx[n1+0], idx[n2+npts-1], idx[n2+0] );
for ( ++k; k<npts; ++k ) {
addTriangle( idx[n1+k-1], idx[n2+k-1], idx[n1+k] );
addTriangle( idx[n1+k], idx[n2+k-1], idx[n2+k] );
}
}
// Log.v("DistoX", "nr. triangles [2] " + mTriangles.size() );
}
// select a triangle that contains the (x,y) scene point
SketchTriangle selectTriangleAt( float x, float y, Sketch3dInfo info, SketchTriangle tri )
{
// Log.v("DistoX", "SketchSurface::selectTriangleAt() " + x + " " + y );
if ( tri != null ) {
if ( tri.contains( x, y ) > 0 ) return tri;
// else try the sides
// SketchTriangle tri2 = mSides.get(tri.sjk).otherTriangle( tri );
// if ( tri2 != null && tri2.contains(x,y) > 0 ) return tri2;
// tri2 = mSides.get(tri.ski).otherTriangle( tri );
// if ( tri2 != null && tri2.contains(x,y) > 0 ) return tri2;
// tri2 = mSides.get(tri.sij).otherTriangle( tri );
// if ( tri2 != null && tri2.contains(x,y) > 0 ) return tri2;
}
SketchTriangle ret = null;
float d0 = 0.0f;
for ( SketchTriangle tri1 : mTriangles ) {
if ( tri1.contains( x, y ) > 0 ) {
float d = tri1.dotNormal( info.ne, info.ns, info.nv );
if ( d > d0 ) {
ret = tri1;
d0 = d;
}
}
}
return ret;
}
// stretch inside triangles
void makeStretch( ArrayList< Vector > pts, ArrayList< Vector > border3d )
{
// Log.v("DistoX", "make stretch pts " + pts.size() + " border 3d " + border3d.size() );
int kmax = pts.size();
float lens[] = new float[ kmax + 1 ];
float len_max = 0.01f;
lens[0] = len_max;
for ( int k=1; k<kmax; ++k ) {
len_max += pts.get(k).distance( pts.get(k-1) );
lens[k] = len_max;
}
lens[ kmax ] = len_max + 1;
synchronized( mTriangles ) {
mInsideVertices.clear();
for ( SketchTriangle t : mInsideTriangles ) {
if ( t.inside ) {
t.inside = false;
if ( ! mInsideVertices.contains( t.v1 ) ) mInsideVertices.add( t.v1 );
if ( ! mInsideVertices.contains( t.v2 ) ) mInsideVertices.add( t.v2 );
if ( ! mInsideVertices.contains( t.v3 ) ) mInsideVertices.add( t.v3 );
}
}
float dist_max = 0;
for ( SketchVertex v : mInsideVertices ) {
// compute min-distance from v to the 3d border
float dist = v.distance( border3d.get(0) );
for ( Vector w : border3d ) {
float d = v.distance( w );
if ( d < dist ) dist = d;
}
if ( dist > dist_max ) dist_max = dist;
v.dist = dist;
}
Vector w0 = pts.get(0);
for ( SketchVertex v : mInsideVertices ) {
float l = len_max * v.dist / dist_max;
int k=0;
while ( k < kmax && l > lens[k] ) ++k;
if ( k == kmax ) -- k;
// Log.v("DistoX", "vector len " + l + "/" + len_max + " shift " + k + "/" + kmax );
Vector w = pts.get(k);
v.x += TDSetting.mDeltaExtrude*(w.x - w0.x);
v.y += TDSetting.mDeltaExtrude*(w.y - w0.y);
v.z += TDSetting.mDeltaExtrude*(w.z - w0.z);
}
}
}
private void drawBorders( Canvas canvas, Matrix matrix, Sketch3dInfo info, int activity_mode )
{
if ( mBorders != null ) {
for ( SketchBorder brd : mBorders ) {
PointF p0 = new PointF();
Path path3 = new Path();
for ( SketchSide s : brd.sides ) {
SketchVertex v1 = mVertices.get( s.v1 );
SketchVertex v2 = mVertices.get( s.v2 );
if ( v1 != null && v2 != null ) {
info.worldToSceneOrigin( v1, p0 );
path3.moveTo( p0.x, p0.y );
info.worldToSceneOrigin( v2, p0 );
path3.lineTo( p0.x, p0.y );
}
}
path3.transform( matrix );
canvas.drawPath( path3, mPainter.borderLinePaint );
}
}
}
private void drawSurfaceBack( Canvas canvas, Matrix matrix, Sketch3dInfo info, int activity_mode )
{
float radius = 5f / info.zoom_3d;
synchronized( mTriangles ) {
// mCorners.clear();
Paint paint = mPainter.surfaceBackPaint;
for ( SketchTriangle tri : mTriangles ) {
if ( info.isForward( tri ) ) continue;
// if ( tri.direction( info.ne, info.ns, info.nv ) < 0.0 ) continue;
float zz = computeProjections( tri, info );
if ( zz > -900 ) {
Path path = new Path();
// if ( activity_mode == SketchDef.MODE_SELECT || activity_mode == SketchDef.MODE_STEP ) {
// mCorners.put( tri.i, tri.p1 );
// mCorners.put( tri.j, tri.p2 );
// mCorners.put( tri.k, tri.p3 );
// }
path.moveTo( tri.p1.x, tri.p1.y );
path.lineTo( tri.p2.x, tri.p2.y );
path.lineTo( tri.p3.x, tri.p3.y );
path.lineTo( tri.p1.x, tri.p1.y );
path.transform( matrix );
paint.setAlpha( 32 + (int)(164/(1+Math.abs(zz) ) ) );
canvas.drawPath( path, paint );
}
}
// paint = mPainter.backVertexPaint;
// int size = mCorners.size();
// for ( int k=0; k<size; ++k ) {
// PointF p = mCorners.valueAt( k );
// Path path = new Path();
// path.addCircle( p.x, p.y, 3*radius, Path.Direction.CCW );
// path.transform( matrix );
// canvas.drawPath( path, paint );
// }
}
}
private void drawSurfaceFor( Canvas canvas, Matrix matrix, Sketch3dInfo info, int activity_mode )
{
Paint highpaint = mPainter.bluePaint;
float radius = 5f / info.zoom_3d;
synchronized( mTriangles ) {
Paint paint = mPainter.surfaceForPaint;
mCorners.clear();
for ( SketchTriangle tri : mTriangles ) {
if ( ! info.isForward( tri ) ) continue;
// if ( tri.direction( info.ne, info.ns, info.nv ) > 0.0 ) continue;
float zz = computeProjections( tri, info );
if ( zz > -900 ) {
Path path = new Path();
// if select mode add triangle vertices
if ( activity_mode == SketchDef.MODE_SELECT ) {
mCorners.put( tri.i, tri.p1 );
mCorners.put( tri.j, tri.p2 );
mCorners.put( tri.k, tri.p3 );
}
path.moveTo( tri.p1.x, tri.p1.y );
path.lineTo( tri.p2.x, tri.p2.y );
path.lineTo( tri.p3.x, tri.p3.y );
path.lineTo( tri.p1.x, tri.p1.y );
path.transform( matrix );
paint.setAlpha( (int)(255/(1+Math.abs(zz)/4 ) ) );
if ( tri.highlight ) {
canvas.drawPath( path, highpaint );
} else {
canvas.drawPath( path, paint );
}
}
}
paint = mPainter.vertexPaint;
int size = mCorners.size();
for ( int k=0; k<size; ++k ) {
PointF p = mCorners.valueAt( k );
Path path = new Path();
path.addCircle( p.x, p.y, radius, Path.Direction.CCW );
path.transform( matrix );
canvas.drawPath( path, paint );
}
}
}
private void drawVertices( Canvas canvas, Matrix matrix, Sketch3dInfo info, int activity_mode )
{
float radius = 5f / info.zoom_3d;
if ( mSelectedVertex != null ) {
synchronized( mSelectedVertex ) {
PointF p = new PointF();
float z = info.worldToSceneOrigin( mSelectedVertex, p );
Path path = new Path();
path.addCircle( p.x, p.y, 2*radius, Path.Direction.CCW );
path.transform( matrix );
canvas.drawPath( path, mPainter.vertexPaint );
}
}
}
// DEBUG show inside triangles blue
private void drawInsideTriangles( Canvas canvas, Matrix matrix, Sketch3dInfo info )
{
if ( mInsideTriangles != null ) {
synchronized( mInsideTriangles ) {
for ( SketchTriangle t : mInsideTriangles ) {
Path path = new Path();
path.moveTo( t.p1.x, t.p1.y );
path.lineTo( t.p2.x, t.p2.y );
path.lineTo( t.p3.x, t.p3.y );
path.lineTo( t.p1.x, t.p1.y );
path.transform( matrix );
canvas.drawPath( path, mPainter.insidePaint );
}
}
}
}
// if ( mSelectedTriangle != null ) {
// Path path1 = new Path();
// SketchVertex v1 = mVertices.get( mSelectedTriangle.i );
// SketchVertex v2 = mVertices.get( mSelectedTriangle.j );
// SketchVertex v3 = mVertices.get( mSelectedTriangle.k );
// PointF p1 = mSelectedTriangle.p1;
// PointF p2 = mSelectedTriangle.p2;
// PointF p3 = mSelectedTriangle.p3;
//
// x1 = v1.x - info.east;
// y1 = v1.y - info.south;
// z1 = v1.z - info.vert;
// // project on (cos_clino*sin_azi, -cos_clino*cos_azimuth, -sin_clino)
// info.worldToScene( x1, y1, z1, p1 );
// path1.moveTo( p1.x, p1.y );
// x1 = v2.x - info.east;
// y1 = v2.y - info.south;
// z1 = v2.z - info.vert;
// info.worldToScene( x1, y1, z1, p2 );
// path1.lineTo( p2.x, p2.y );
// x1 = v3.x - info.east;
// y1 = v3.y - info.south;
// z1 = v3.z - info.vert;
// info.worldToScene( x1, y1, z1, p3 );
// path1.lineTo( p3.x, p3.y );
// path1.lineTo( p1.x, p1.y );
// path1.transform( matrix );
// canvas.drawPath( path1, mPainter.redPaint );
// }
synchronized void draw( Canvas canvas, Matrix matrix, Sketch3dInfo info, int activity_mode )
{
drawSurfaceBack( canvas, matrix, info, activity_mode );
drawSurfaceFor( canvas, matrix, info, activity_mode );
drawVertices( canvas, matrix, info, activity_mode );
// drawInsideTriangles( canvas, matrix, info, activity_mode );
drawBorders( canvas, matrix, info, activity_mode );
}
// get the vertex at a certain scene point
// (null if no vertex found)
SketchVertex getVertexAt( float x, float y, float d ) // (x,y) scene point
{
synchronized( mTriangles ) { // mCorners is synchronized on mTriangles
int size = mCorners.size();
for ( int k=0; k<size; ++k ) {
PointF p = mCorners.valueAt( k );
int key = mCorners.keyAt( k );
// Log.v("DistoX", "pt " + key + " " + p.x + " " + p.y );
if ( Math.abs( p.x - x ) < d && Math.abs( p.y - y ) < d ) {
return mVertices.get( key );
}
}
}
return null;
}
void toTherion( PrintWriter pw, String what )
{
// dump();
// pw.format("%s -shot %s %s %d %d %d\n", what, st1, st2, mVertices.size(), mSides.size(), mTriangles.size() );
pw.format("%s -shot %s %s %d %d\n", what, st1, st2, mVertices.size(), mTriangles.size() );
pw.format(" vertex\n");
int size = mVertices.size();
for ( int k = 0; k < size; ++k ) {
SketchVertex v = mVertices.valueAt( k );
v.toTherion( pw );
}
pw.format(" endvertex\n");
// pw.format(" side\n");
// for ( SketchSide s : mSides.values() ) {
// s.toTherion( pw );
// }
// pw.format(" endside\n");
pw.format(" triangle\n");
for ( SketchTriangle t : mTriangles ) {
t.toTherion( pw );
}
pw.format(" endtriangle\n");
pw.format("end%s\n\n", what );
}
}