/** * Gives a clone a new direction (splitting) * * @param trf The base transformation of the clone * @param s_angle The splitting angle * @param nseg The segment number, where the clone begins * @param nsplits The number of clones * @return The new direction for the clone */ DX_Transformation split(DX_Transformation trf, float s_angle, int nseg, int nsplits) { // applies a split angle to the stem - the Weber/Penn method int remaining_seg = segmentCount - nseg - 1; // the splitangle // FIXME: don't know if it should be nSplitAngle or nSplitAngle/2 float declination = (float)(Math.Acos(trf.getZ3().Z) * 180 / Math.PI); float split_angle = Math.Max(0, (lpar.nSplitAngle + lpar.var(lpar.nSplitAngleV) - declination)); // FIXME: first works better for level 0, second for further levels // transf = transf.rotxz(split_angle,s_angle) trf = trf.rotx(split_angle); // adapt split correction splitCorrection -= split_angle / remaining_seg; //t_corr = Transformation().rotx(-split_angle/remaining_seg) float split_diverge; if (s_angle > 0) { // original stem has s_angle==0 if (par._0BaseSplits > 0 && stemlevel == 0 && nseg == 0) { split_diverge = s_angle + lpar.var(lpar.nSplitAngleV); } else { split_diverge = (float)(20 + 0.75 * (30 + Math.Abs(declination - 90)) * Math.Pow((lpar.var(1) + 1) / 2.0, 2)); if (lpar.var(1) >= 0) { split_diverge = -split_diverge; } } trf = trf.rotaxis(split_diverge, DX_Transformation.Z_AXIS); } else { split_diverge = 0; // for debugging only } // adjust some parameters //split_cnt = split_cnt+1; // lower substem prospensity if (!pruneTest) { substemsPerSegment /= (float)(nsplits + 1); // FIXME: same reduction for leaves_per_segment? } return(trf); }
/** * Leaf rotation toward light */ private void setLeafOrientation(CS_Params par) { if (par.LeafBend == 0) { return; } // FIXME: make this function as fast as possible - a tree has a lot of leafs // rotation outside Vector3 pos = transf.getT(); // the z-vector of transf is parallel to the // axis of the leaf, the y-vector is the normal // (of the upper side) of the leaf Vector3 norm = transf.getY3(); float tpos = (float)(Math.Atan2(pos.Y, pos.X) * 180 / Math.PI); float tbend = tpos - (float)(Math.Atan2(norm.Y, norm.X) * 180 / Math.PI);; // if (tbend>180) tbend = 360-tbend; float bend_angle = par.LeafBend * tbend; // transf = transf.rotz(bend_angle); // rotate about global z-axis transf = transf.rotaxis(bend_angle, DX_Transformation.Z_AXIS); // rotation up norm = transf.getY3(); float fbend = (float)(Math.Atan2((float)Math.Sqrt(norm.X * norm.X + norm.Y * norm.Y), norm.Z) * 180 / Math.PI); bend_angle = par.LeafBend * fbend; transf = transf.rotx(bend_angle); // this is from the paper, but is equivalent with // local x-rotation (upper code line) // // double orientation = Vector.atan2(norm.getY(),norm.getX()); // transf = transf // .rotaxis(-orientation,Vector.Z_AXIS) // .rotx(bend_angle) // .rotaxis(orientation,Vector.Z_AXIS); }
/** * Calcs a new direction for the current segment * * @param trf The transformation of the previous segment * @param nsegm The number of the segment ( for testing, if it's the * first stem segment * @return The new transformation of the current segment */ DX_Transformation newDirection(DX_Transformation trf, int nsegm) { // next segments direction // The first segment shouldn't get another direction // down and rotation angle shouldn't be falsified if (nsegm == 0) { return(trf); } /* * if (Console.debug()) * TRF("Stem.new_direction() before curving",trf); */ // get curving angle double delta; if (lpar.nCurveBack == 0) { delta = lpar.nCurve / lpar.nCurveRes; } else { if (nsegm < (lpar.nCurveRes + 1) / 2) { delta = lpar.nCurve * 2 / lpar.nCurveRes; } else { delta = lpar.nCurveBack * 2 / lpar.nCurveRes; } } delta += splitCorrection; /* * if (Console.debug()) * DBG("Stem.new_direction(): delta: "+delta); */ trf = trf.rotx(delta); // With Weber/Penn the orientation of the x- and y-axis // shouldn't be disturbed (maybe, because proper curving relies on this) // so may be such random rotations shouldn't be used, instead nCurveV should // add random rotation to rotx, and rotate nCurveV about the tree's z-axis too? // add random rotation about z-axis if (lpar.nCurveV > 0) { // if (nsegm==0 && stemlevel==0) { // first_trunk_segment // // random rotation more moderate // delta = (Math.abs(lpar.var(lpar.nCurveV)) - // Math.abs(lpar.var(lpar.nCurveV))) // / lpar.nCurveRes; // } else { // full random rotation delta = lpar.var(lpar.nCurveV) / lpar.nCurveRes; // } // self.DBG("curvV (delta): %s\n" % str(delta)) double rho = 180 + lpar.var(180); trf = trf.rotaxisz(delta, rho); } //TRF("Stem.new_direction() after curving",trf); // attraction up/down if (par.AttractionUp != 0 && stemlevel >= 2) { double declination = Math.Acos(trf.getZ3().Z); // I don't see, why we need orientation here, may be this avoids // attraction of branches with the x-Axis up and thus avoids // twisting (see below), but why branches in one direction should // be attracted, those with another direction not, this is unnaturally: // double orient = Math.acos(trf.getY().getZ()); // double curve_up_orig = par.AttractionUp * declination * Math.cos(orient)/lpar.nCurveRes; // FIXME: devide by (lpar.nCurveRes-nsegm) if last segment // should actually be vertical double curve_up = par.AttractionUp * Math.Abs(declination * Math.Sin(declination)) / lpar.nCurveRes; Vector3 z = trf.getZ3(); // FIXME: the mesh is twisted for high values of AttractionUp trf = trf.rotaxis(-curve_up * 180 / Math.PI, new Vector3(-z.Y, z.X, 0)); // trf = trf.rotx(curve_up*180/Math.PI); } return(trf); }
/** * Creates the leaves for the current stem segment * * @param segment */ void makeLeaves(CS_SegmentImpl segment) { // creates leaves for the current segment if (par.Leaves > 0) { // ### NORMAL MODE, leaves along the stem // how many leaves in this segment float leaves_eff = (int)(leavesPerSegment + par.leavesErrorValue + 0.5); // adapt error value par.leavesErrorValue -= (leaves_eff - leavesPerSegment); if (leaves_eff <= 0) { return; } float offs; if (segment.index == 0) { offs = parent.stemRadius(offset) / segmentLength; } else { offs = 0; } // what distance between the leaves float dist = (1.0f - offs) / leaves_eff; for (int s = 0; s < leaves_eff; s++) { // where on the segment add the leaf // FIXME: may be use the same distribution method (BranchDist) as for substems? float where = offs + dist / 2 + s * dist + lpar.var(dist / 2); // offset from stembase float loffs = (segment.index + where) * segmentLength; // get a new direction for the leaf DX_Transformation trf = substemDirection(segment.transf, loffs); // translate it to its position on the stem trf = trf.translate(segment.transf.getZ3() * (where * segmentLength)); // create new leaf CS_LeafImpl leaf = new CS_LeafImpl(trf); // ,loffs); leaf.make(par); leaves.Add(leaf); } } // ##### FAN MOD, leaves placed in a fan at stem end else if (par.Leaves < 0 && segment.index == segmentCount - 1) { CS_LevelParams lpar_1 = par.getLevelParams(stemlevel + 1); int cnt = (int)(leavesPerBranch() + 0.5); DX_Transformation trf = segment.transf.translate(segment.transf.getZ3() * (segmentLength)); float distangle = lpar_1.nRotate / cnt; float varangle = lpar_1.nRotateV / cnt; float downangle = lpar_1.nDownAngle; float vardown = lpar_1.nDownAngleV; float offsetangle = 0; // use different method for odd and even number if (cnt % 2 == 1) { // create one leaf in the middle CS_LeafImpl leaf = new CS_LeafImpl(trf); //,segmentCount*segmentLength); leaf.make(par); leaves.Add(leaf); offsetangle = distangle; } else { offsetangle = distangle / 2; } // create leaves left and right of the middle for (int s = 0; s < cnt / 2; s++) { for (int rot = 1; rot >= -1; rot -= 2) { DX_Transformation transf1 = trf.roty(rot * (offsetangle + s * distangle + lpar_1.var(varangle))); transf1 = transf1.rotx(downangle + lpar_1.var(vardown)); CS_LeafImpl leaf = new CS_LeafImpl(transf1); //,segmentCount*segmentLength); leaf.make(par); leaves.Add(leaf); } } } }