protected override TrTransform CalculateChildXf(List <PbKnot> parentKnots) { TrTransform actionInCanvasSpace; { PbKnot knot = GetAttachKnot(parentKnots); float rotationDegrees = knot.m_distance * App.UNITS_TO_METERS * m_twist; TrTransform offset = m_offset; // It's cleaner to tack a TrTransform.S(size) onto the action, but that // would modify .scale, which is currently interpreted as "pointer size" // (affecting control point density) and which is also assumed by most // brushes to be constant. if (m_pressureAffectsOffset) { offset.translation *= knot.m_pressuredSize; } else { offset.translation *= m_brush.BaseSize_LS; } TrTransform action = TrTransform.R(Quaternion.AngleAxis(rotationDegrees, Vector3.forward)) * offset; actionInCanvasSpace = action.TransformBy(knot.GetFrame(m_frame)); } var cur = parentKnots[parentKnots.Count - 1]; return(actionInCanvasSpace * cur.m_pointer); }
// A simplified version of TwoPointObjectTransformation. The following properties are true: // 1. The object-local-space direction between the left and right hands remains constant. // 2. The object-local-space position of LerpUnclamped(left, right, constraintPositionT) remains // constant. // 3. obj1 has the same scale as obj0. // 4. (Corollary of 1-3) The object-local-space positions of left and right remain constant, if // the distance between them does not change. public static TrTransform TwoPointObjectTransformationNoScale( TrTransform gripL0, TrTransform gripR0, TrTransform gripL1, TrTransform gripR1, TrTransform obj0, float constraintPositionT) { // Vectors from left-hand to right-hand Vector3 vLR0 = (gripR0.translation - gripL0.translation); Vector3 vLR1 = (gripR1.translation - gripL1.translation); Vector3 pivot0; TrTransform xfDelta; { pivot0 = Vector3.LerpUnclamped(gripL0.translation, gripR0.translation, constraintPositionT); var pivot1 = Vector3.LerpUnclamped(gripL1.translation, gripR1.translation, constraintPositionT); xfDelta.translation = pivot1 - pivot0; xfDelta.translation = Vector3.LerpUnclamped( gripL1.translation - gripL0.translation, gripR1.translation - gripR0.translation, constraintPositionT); // TODO: check edge cases: // - |vLR0| or |vLR1| == 0 (ie, from and/or to are undefined) // - vLR1 == vLR0 * -1 (ie, infinite number of axes of rotation) xfDelta.rotation = Quaternion.FromToRotation(vLR0, vLR1); xfDelta.scale = 1; } Quaternion deltaL = ConstrainRotationDelta(gripL0.rotation, gripL1.rotation, vLR0); Quaternion deltaR = ConstrainRotationDelta(gripR0.rotation, gripR1.rotation, vLR0); xfDelta = TrTransform.R(Quaternion.Slerp(deltaL, deltaR, 0.5f)) * xfDelta; // Set pivot point xfDelta = xfDelta.TransformBy(TrTransform.T(pivot0)); return(xfDelta * obj0); }
public void TestTransformByForBasisChange() { // This is more a test of TrTransform.TransformBy than anything else var zFromU = VrAssetService.kPolyFromUnity; // This rotates Unity-forward to Unity-right TrTransform xfFwdToRt_U = TrTransform.R(Quaternion.AngleAxis(90, Vector3.up)); AssertAlmostEqual(Vector3.right, xfFwdToRt_U * Vector3.forward); AssertAlmostEqual(Vector3.up, xfFwdToRt_U * Vector3.up); // This should rotate Poly-forward to Poly-right (to test) TrTransform xfFwdToRt_Z = xfFwdToRt_U.TransformBy(zFromU); AssertAlmostEqual(kZRight, xfFwdToRt_Z * kZForward); AssertAlmostEqual(kZUp, xfFwdToRt_Z * kZUp); }
protected override void MaybeCreateChildrenImpl() { // Only create children once if (m_children.Count > 1) { return; } BrushDescriptor childDesc = m_Desc; if (m_recursionLevel == 1) { childDesc = m_baseBrush; } // Since we're doing these color modifications, doing reflection or rotation first // changes the look. if (m_recursionLevel == 0) { // Reflection HSLColor reflectedColor = m_Color; reflectedColor.s += m_saturationDelta * ((reflectedColor.s > 0.5f) ? -1 : 1); InitializeAndAddChild(new PbChildIdentityXf(), childDesc, m_Color); InitializeAndAddChild( new PbChildKnotBasedMirror(0, AttachFrame.Pointer, kReflectionPlane), childDesc, (Color)reflectedColor); } else if (m_recursionLevel == 1) { // Rotation float rotationDeltaDegrees = 360f / m_numRotations; for (int i = 0; i < m_numRotations; ++i) { int iWrapped = i + ((i * 2 > m_numRotations) ? -m_numRotations : 0); HSLColor rotatedColor = m_Color; float angle = iWrapped * rotationDeltaDegrees; rotatedColor.HueDegrees += m_hueDeltaPct * angle; TrTransform offset = TrTransform.R(angle, Vector3.forward); InitializeAndAddChild( new PbChildWithOffset(0, AttachFrame.Pointer, offset, 0), childDesc, (Color)rotatedColor); } } }
protected override TrTransform CalculateChildXf(List <PbKnot> parentKnots) { PbKnot lastKnot = parentKnots[parentKnots.Count - 1]; float distanceMeters = lastKnot.m_distance * App.UNITS_TO_METERS; float t = O.CyclesPerMeter * distanceMeters + (float)m_strand / O.NumStrands; // Our periodic function makes the plait look pretty square; maybe add // some rotation to break things up a bit? float rotations = (O.CyclesPerMeter * distanceMeters) * O.RotationsPerCycle; float amplitude = lastKnot.m_pressuredSize / 2; // /2 because size is diameter, not radius. TrTransform action = TrTransform.R(rotations * 360, Vector3.forward) * TrTransform.T(SomePeriodicFunction(t) * amplitude); TrTransform actionInCanvasSpace = action.TransformBy(lastKnot.GetFrame(AttachFrame.LineTangent)); return(actionInCanvasSpace * lastKnot.m_pointer); }
public void TestMengerCurvatureNonDegenerate() { float kRadius = 2.5f; float kInvRadius = 1 / kRadius; Vector3 radius = new Vector3(kRadius, 0, 0); Vector3 axis = new Vector3(0, 1, 0); // should be perpendicular to radius TrTransform arbitrary = TrTransform.TR( new Vector3(5, 8, -10), Quaternion.AngleAxis(35, new Vector3(-1, 3, -5))); // Pick a bunch of triplets on a circle of radius r. // Points are chosen by selecting two arc angles a, b, and computing the third arc angle c. // Repetitions are avoided by choosing a, b, c such that a <= b <= c. // Degenerate cases are avoided by choosing a > 0 (implying a, b, c > 0). // If a > 120, one of b or c will always be smaller than a for (int a = 12; a <= 120; a += 24) { for (int b = a; /*b <= c*/; b += 24) { int c = 360 - a - b; if (!(b <= c)) { break; } Vector3 va = TrTransform.R(0, axis) * radius; Vector3 vb = TrTransform.R(a, axis) * radius; Vector3 vc = TrTransform.R(a + b, axis) * radius; AssertAlmostEqual(kInvRadius, MathUtils.MengerCurvature(va, vb, vc)); Vector3 va2 = arbitrary * va; Vector3 vb2 = arbitrary * vb; Vector3 vc2 = arbitrary * vc; AssertAlmostEqual(kInvRadius, MathUtils.MengerCurvature(va2, vb2, vc2)); } } }
/// Spawn a model. /// Precondition: Model must have m_Valid == true. protected void SpawnValidModel(Model model) { if (!model.m_Valid) { throw new InvalidOperationException("model must be valid"); } // Button forward is into the panel, not out of the panel; so flip it around TrTransform xfSpawn = Coords.AsGlobal[transform] * TrTransform.R(Quaternion.AngleAxis(180, Vector3.up)); CreateWidgetCommand createCommand = new CreateWidgetCommand( WidgetManager.m_Instance.ModelWidgetPrefab, xfSpawn, m_PreviewBaseRotation); SketchMemoryScript.m_Instance.PerformAndRecordCommand(createCommand); ModelWidget modelWidget = createCommand.Widget as ModelWidget; modelWidget.Model = model; modelWidget.Show(true); createCommand.SetWidgetCost(modelWidget.GetTiltMeterCost()); WidgetManager.m_Instance.WidgetsDormant = false; SketchControlsScript.m_Instance.EatGazeObjectInput(); SelectionManager.m_Instance.RemoveFromSelection(false); }
protected override void MaybeCreateChildrenImpl() { // TODO: when too many children, starve an old one in order to create another. int kBranchCount = 12; int kFrondCount = 16; if (m_recursionLevel == 0) { // Trunk if (m_children.Count == 0) { InitializeAndAddChild(new PbChildIdentityXf(), m_trunkBrush, m_trunkColor); } if (DistanceSinceLastKnotBasedChild() > m_branchFrequency && m_children.Count < kBranchCount + 1) { int salt = m_children.Count; // Children 1 - 13 are the branches; early ones grow faster. float growthPercent = (kBranchCount + 1 - (float)m_children.Count) / kBranchCount; TrTransform offset = // Branches don't extend as quickly as the trunk TrTransform.S(m_branchScale * growthPercent) * // Randomly place around the tree TrTransform.R(m_rng.InRange(salt, 0f, 360f), Vector3.forward) * // Angle the branches backwards (away from the stroke tip) TrTransform.R(120, Vector3.right); InitializeAndAddChild( new PbChildWithOffset(m_knots.Count - 1, AttachFrame.LineTangent, offset, 0), m_Desc, // Recurse with same brush Color.white, m_branchRelativeSize); } } else if (m_recursionLevel == 1) { // Branch if (m_children.Count == 0) { InitializeAndAddChild(new PbChildIdentityXf(), m_branchBrush, m_branchColor); } // TODO: would like this frequency to be higher for tinier branches if (DistanceSinceLastKnotBasedChild() > m_frondFrequency && m_children.Count < kFrondCount + 1) { float growthPercent = 1; // (kFrondCount + 1 - (float)m_children.Count) / kFrondCount; for (int deg = -30; deg <= 30; deg += 60) { TrTransform offset = // Fronds don't grow as quickly as the branch TrTransform.S(m_frondScale * growthPercent) * TrTransform.R(deg, Vector3.up); InitializeAndAddChild( new PbChildWithOffset(m_knots.Count - 1, AttachFrame.LineTangent, offset, 0), m_frondBrush, m_frondColor, m_frondRelativeSize); TrTransform decoOffset = TrTransform.T(m_decoOffset); InitializeAndAddChild( new PbChildWithOffset(-1, AttachFrame.LineTangent, decoOffset, m_decoTwist), m_decoBrush, Color.white, m_frondRelativeSize); } } } }
protected override IEnumerable <ControlPoint> DoGetPoints(TrTransform finalTransform) { double t0 = m_initialTime; double t1 = App.Instance.CurrentSketchTime; Vector3 center = m_initialTransform.translation; Vector3 nRadius = finalTransform.translation - center; float fRadius = nRadius.magnitude; nRadius /= fRadius; // Degenerate circle -- turn it into a line if (fRadius < 1e-5f) { yield return(new ControlPoint { m_Pos = m_initialTransform.translation, m_Orient = m_initialTransform.rotation, m_Pressure = 1f, m_TimestampMs = (uint)(t0 * kSecondsToMs) }); yield return(new ControlPoint { m_Pos = finalTransform.translation, m_Orient = finalTransform.rotation, m_Pressure = 1f, m_TimestampMs = (uint)(t1 * kSecondsToMs) }); yield break; } // Tangent must be perpendicular to nRadius var thisState = new ComputeTangentState { nRadius = nRadius, rotation = finalTransform.rotation, preferred = m_vPreferredTangent }; Vector3 nTangent = ComputeTangent(thisState); m_vPreferredTangent = nTangent; #if DEBUG_TANGENT if (m_oldState != null) { Vector3 nOldTangent = ComputeTangent(m_oldState.Value); if (fRadius > .5f && Vector3.Dot(nOldTangent, nTangent) < .966f) { int nn = 20; for (int i = 0; i < nn; ++i) { Vector3 v0 = ComputeTangent(thisState); Vector3 v1 = ComputeTangent(m_oldState.Value); } } } m_oldState = thisState; #endif // Axis is perpendicular to tangent and radius // TODO: experiment with removing this restriction? Vector3 nAxis = Vector3.Cross(nTangent, nRadius).normalized; TrTransform xf0 = finalTransform; // TODO: adjust control point density int n = 30; // number of points; must be >= 2 for (int i = 0; i <= n; ++i) { float t = (float)i / n; Quaternion rot = Quaternion.AngleAxis(360 * t, nAxis); TrTransform delta = TrTransform.R(rot).TransformBy(TrTransform.T(center)); TrTransform xf = delta * xf0; yield return(new ControlPoint { m_Pos = xf.translation, m_Orient = xf.rotation, m_Pressure = 1f, m_TimestampMs = (uint)(Mathf.Lerp((float)t0, (float)t1, t) * 1000) }); } #if DEBUG_TANGENT var start = finalTransform.translation; foreach (var val in DrawLine(start, nTangent, 5)) { yield return(val); } foreach (var val in DrawLine(start, nAxis, 7)) { yield return(val); } #endif }