/** * Calcs the position of a substem in the segment given * a relativ position where in 0..1 - needed esp. for helical stems, * because the substems doesn't grow from the axis of the segement * * @param trf the transformation of the substem * @param where the offset, where the substem spreads out * @return the new transformation of the substem (shifted from * the axis of the segment to the axis of the subsegment) */ public DX_Transformation substemPosition(DX_Transformation trf, float where) { if (lpar.nCurveV >= 0) // normal segment { return(trf.translate(transf.getZ3() * (where * length))); } else // helix // get index of the subsegment { int i = (int)(where * (subsegments.Count - 1)); // interpolate position Vector3 p1 = ((CS_SubsegmentImpl)subsegments[i]).pos; Vector3 p2 = ((CS_SubsegmentImpl)subsegments[i + 1]).pos; Vector3 pos = p1 + (p2 - p1) * (where - i / (subsegments.Count - 1)); return(trf.translate(pos - getLowerPosition())); } }
/** * 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); }
/** * 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); }
// makes the segments of the stem int makeSegments(int start_seg, int end_seg) { // if (start_seg>end_seg) throw new ArbaroException("Error in segment creation end_seg<start_seg."); if (stemlevel == 1) { tree.updateGenProgress(); } //if (par.verbose) { /* * if (! pruneTest) { * if (stemlevel==0) Console.progressChar('='); * else if (stemlevel==1 && start_seg==0) Console.progressChar('/'); * else if (stemlevel==2 && start_seg==0) Console.progressChar(); * } * //} */ DX_Transformation trf = transf; for (int s = start_seg; s < end_seg; s++) { if (stemlevel == 0) { tree.updateGenProgress(); } if (!pruneTest) {// && par.verbose) { //if (stemlevel==0) Console.progressChar('|'); } // curving trf = newDirection(trf, s); /* * if (Console.debug()) * TRF("Stem.make_segments(): after new_direction ",trf); */ // segment radius float rad1 = stemRadius(s * segmentLength); float rad2 = stemRadius((s + 1) * segmentLength); // create new segment CS_SegmentImpl segment = new CS_SegmentImpl(this, s, trf, rad1, rad2); segment.make(); segments.Add(segment); // create substems // self.DBG("SS-makingsubst? pt: %d, lev: %d\n"%(self.prunetest,self.level)) if (!pruneTest && lpar.level < par.Levels - 1) { // self.DBG("SS-making substems\n") makeSubstems(segment); } if (!pruneTest && lpar.level == par.Levels - 1 && par.Leaves != 0) { makeLeaves(segment); } // shift to next position trf = trf.translate(trf.getZ3() * (segmentLength)); //self.DBG("transf: %s\n"%(transf)) //self.DBG("pos: %s\n"%(transf.vector)) // test if too long if (pruneTest && !isInsideEnvelope(trf.getT())) { // DBG("PRUNE: not inside - return %d\n"%(s)) return(s); } // splitting (create clones) if (s < end_seg - 1) { int segm = makeClones(trf, s); // trf is changed by make_clones // prune test - clone not inside envelope if (segm >= 0) { //DBG("PRUNE: clone not inside - return %d\n"%(segm)) return(segm); } } } return(-1); }