/** @file SketchTriangle.java * * @author marco corvi * @date feb 2013 * * @brief TopoDroid 3d sketch: 3D surface triangle * -------------------------------------------------------- * Copyright This sowftare is distributed under GPL-3.0 or later * See the file COPYING. * -------------------------------------------------------- */ package com.topodroid.DistoX; import android.graphics.PointF; // import java.io.StringWriter; import java.io.PrintWriter; import java.util.List; import java.util.ArrayList; import java.util.Locale; import android.util.Log; class SketchTriangle { // int type; // 1: (n1, n1+1, n2), 2: (n1, n2+1, n2), 0: others boolean highlight; // whether this triangle is highlighted boolean inside; // whether this triangle is inside the edit-border boolean splitted; // whether this triangle has been splitted int i, j, k; // vertex indices // int sjk, ski, sij; // side indices ( side sij is opposite to vertex k ) SketchVertex v1, v2, v3; SketchSurface surface; Vector center; // 3D center of the triangle Vector normal; PointF p1, p2, p3; // canvas-projected vertex points (scene coords) float cosine1; // cosine angle v2-v1-v3 float cosine2; float cosine3; Vector w12; // unit vector (v1 - v2) Vector w23; // unit vector (v2 - v3) Vector w31; // N.B. side s23 is opposite to vertex 1 (i0) SketchTriangle( SketchSurface parent, int i0, int j0, int k0, SketchVertex v10, SketchVertex v20, SketchVertex v30 ) //, // int s23, int s31, int s12 ) { surface = parent; highlight = false; inside = false; i = i0; j = j0; k = k0; v1 = v10; v2 = v20; v3 = v30; // sjk = s23; // ski = s31; // sij = s12; center = new Vector( (v1.x+v2.x+v3.x)/3, (v1.y+v2.y+v3.y)/3, (v1.z+v2.z+v3.z)/3 ); w12 = v1.minus( v2 ); w12.normalize(); w31 = v3.minus( v1 ); w31.normalize(); w23 = v2.minus( v3 ); w23.normalize(); // float x1 = v1.x - v2.x; // float y1 = v1.y - v2.y; // float z1 = v1.z - v2.z; // float x3 = v3.x - v2.x; // float y3 = v3.y - v2.y; // float z3 = v3.z - v2.z; // normal = new Vector( y3*z1-y1*z3, z3*x1-z1*x3, x3*y1-y3*x1 ); normal = w12.cross( w23 ); normal.normalize(); p1 = new PointF(0,0); p2 = new PointF(0,0); p3 = new PointF(0,0); // check // float c1 = normal.x*(v1.x - v2.x) + normal.y*(v1.y - v2.y) + normal.z*(v1.z - v2.z); // float c3 = normal.x*(v3.x - v2.x) + normal.y*(v3.y - v2.y) + normal.z*(v3.z - v2.z); // if ( Math.abs(c1) > 0.01 || Math.abs(c3) > 0.01 ) { // Log.v("DistoX", "fail tri normal: " + c1 + " " + c3 ); // } cosine1 = - w31.dot( w12 ); cosine2 = - w12.dot( w23 ); cosine3 = - w23.dot( w31 ); } // plane: ( X - Xc ) * N = 0 // line: X = W1 + L * (W2 - W1) // intersection: (W1-Xc)*N = L * (W1-W2)*N // ==> L = (W1-Xc)*N / (W1-W2)*N // Vector intersection( Vector w1, Vector w2 ) { float d1 = normal.dot( w1.minus( center ) ); Vector z12 = w1.minus(w2); float d2 = normal.dot( z12 ); if ( d1 * d2 <= 0 || Math.abs(d1) > Math.abs(d2) ) return null; z12.timesEqual( d1/d2 ); Vector w0 = w1.minus( z12 ); Vector w = w0.minus( v1 ); w.normalize(); if ( w31.dot( w ) < cosine1 ) return null; w = w0.minus( v2 ); w.normalize(); if ( w12.dot( w ) < cosine2 ) return null; w = w0.minus( v3 ); w.normalize(); if ( w23.dot( w ) < cosine3 ) return null; // Log.v("DistoX", "intersection with plane at " + -d1/d2 + " vector " + w0.x + " " + w0.y + " " + w0.y ); return w2.minus( w0 ); } void shiftVertices( Vector v ) { v1.plusEqual( v ); v2.plusEqual( v ); v3.plusEqual( v ); center.plusEqual( v ); } // dot product of the normal with a vector float dotNormal( float x, float y, float z ) { return x * normal.x + y * normal.y + z * normal.z; } float dotCenter( float x, float y, float z ) { return x * center.x + y * center.y + z * center.z; } // distance of a 3D point from the center of the triangle float distanceCenter( Vector v ) { return center.distance( v ); } // distance of a 3D point from the plane of the triangle float distancePlane( Vector v ) { return normal.dot( v.minus( center ) ); } // get the 3D point in the plane of the triangle // that corresponds to a 2D scene point inside the projection of the triangle // (a,b,c) barycentric coords of P=(x,y,z): // P = a P1 + b P2 + c P3 // Vector get3dPoint( float x, float y ) // (x,y) scene coords { float y23 = p2.y - p3.y; float y31 = p3.y - p1.y; float y12 = p1.y - p2.y; float x32 = p3.x - p2.x; float x13 = p1.x - p3.x; float x21 = p2.x - p1.x; float det = p1.x*y23 + p2.x*y31 + p3.x*y12; float a = (y23 * x + x32 * y + p2.x*p3.y-p3.x*p2.y)/det; float b = (y31 * x + x13 * y + p3.x*p1.y-p1.x*p3.y)/det; float c = (y12 * x + x21 * y + p1.x*p2.y-p2.x*p1.y)/det; Vector ret = new Vector( a*v1.x+b*v2.x+c*v3.x, a*v1.y+b*v2.y+c*v3.y, a*v1.z+b*v2.z+c*v3.z ); // check // float c3 = normal.x*(ret.x-v2.x) + normal.y*(ret.y-v2.y) + normal.z*(ret.z-v2.z); // if ( Math.abs(c3) > 0.01 || Math.abs(a+b+c-1) > 0.01 ) { // Log.v("DistoX", "fail tri vector normal: " + c3 + " coords " + a + " " + b + " " + c ); // } return ret; } /** check if this triangle contais a scene point X=(x,y) * compute the three cross-products: * (X - P1) ^ (P2 - P1) * (X - P2) ^ (P3 - P2) * (X - P3) ^ (P1 - P3) * if they have all the same sign the point X is on the same side of every * side of the triangle * + P1 * / \ * / \ neg. side * / \ * ------ P2 +--->---+ P3 ------- * pos. side */ int contains( float x, float y ) // (x,y) scene coords { float x1 = x - p1.x; float y1 = y - p1.y; float x2 = p2.x - p1.x; float y2 = p2.y - p1.y; float z1 = x1 * y2 - x2 * y1; x1 = x - p2.x; y1 = y - p2.y; x2 = p3.x - p2.x; y2 = p3.y - p2.y; float z2 = x1 * y2 - x2 * y1; x1 = x - p3.x; y1 = y - p3.y; x2 = p1.x - p3.x; y2 = p1.y - p3.y; float z3 = x1 * y2 - x2 * y1; if ( z1 > 0 && z2 > 0 && z3 > 0 ) return -1; if ( z1 < 0 && z2 < 0 && z3 < 0 ) return 1; return 0; } // check if the canvas-projected triangle is inside the border boolean isInside( ArrayList<PointF> border ) { float a1 = angleAround( border, p1 ); float a2 = angleAround( border, p2 ); float a3 = angleAround( border, p3 ); // StringWriter sw = new StringWriter(); // PrintWriter pw = new PrintWriter( sw ); // pw.format(Locale.US, "T %.2f %.2f %.2f %.2f %.2f %.2f A %.2f %.2f %.2f", // p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, a1, a2, a3 ); // Log.v( "DistoX", sw.getBuffer().toString() ); return Math.abs(a1) > 0.1f && Math.abs(a2) > 0.1f && Math.abs(a3) > 0.1f; } private float angleAround( ArrayList<PointF> border, PointF p ) { float a = 0; int nb = border.size(); PointF q = border.get(nb-1); float x1 = q.x - p.x; float y1 = q.y - p.y; if ( Math.abs(x1) < 0.001 && Math.abs(y1) < 0.001 ) return TDMath.M_PI; for ( int k=0; k<nb; ++k ) { q = border.get(k); float x2 = q.x - p.x; float y2 = q.y - p.y; if ( Math.abs(x2) < 0.001 && Math.abs(y2) < 0.001 ) return TDMath.M_PI; float s = x1*y2 - y1*x2; float c = x1*x2 + y1*y2; a += TDMath.atan2( s, c ); x1 = x2; y1 = y2; } // if ( Math.abs(a) < 0.001 ) a = 0; return a; } // ---------------------------------------------------------------- // THERION three vertex indices and the three opposite side indices // v1 v2 v3 s23 s31 s12 void toTherion( PrintWriter pw ) { // pw.format(" %d %d %d %d %d %d \n", i, j, k, sjk, ski, sij ); pw.format(" %d %d %d\n", i, j, k ); } }