// Evaluates the rules in the given XML stream and returns a list of curves func Evaluate(stream io.Reader) Curve { var curve Curve var lsys LSystem bytes, _ := ioutil.ReadAll(stream) if err := xml.Unmarshal(bytes, &lsys); err != nil { fmt.Println("Error parsing XML file:", err) return curve } // Parse the transform strings lsys.Matrices = make(MatrixCache) for _, rule := range lsys.Rules { for _, call := range rule.Calls { lsys.Matrices.ParseString(call.Transforms) } for _, inst := range rule.Instances { lsys.Matrices.ParseString(inst.Transforms) } } lsys.Matrices[""] = *vmath.M4Identity() // Provide defaults for i := range lsys.Rules { r := &lsys.Rules[i] if r.Weight == 0 { r.Weight = 1 } } // Seed the random-number generator and pick the entry rule random := rand.New(rand.NewSource(42)) start := StackNode{ RuleIndex: lsys.PickRule("entry", random), Transform: vmath.M4Identity(), } lsys.ProcessRule(start, &curve, random) return curve }
// Parse a string in the xform language and add the resulting matrix to the lookup table. // Examples: // "rx -2 tx 0.1 sa 0.996" // "s 0.55 2.0 1.25" func (cache *MatrixCache) ParseString(s string) { if len(s) == 0 { return } reader := bufio.NewReader(strings.NewReader(s)) xform := vmath.M4Identity() readFloat := func() float32 { sx, _ := reader.ReadString(' ') fx, _ := strconv.ParseFloat(strings.TrimSpace(sx), 32) return float32(fx) } for { token, err := reader.ReadString(' ') token = strings.TrimSpace(token) switch token { case "s": x := readFloat() y := readFloat() z := readFloat() m := vmath.M4Scale(x, y, z) xform = m.Compose(xform) case "sa": a := readFloat() m := vmath.M4Scale(a, a, a) xform = m.Compose(xform) case "t": x := readFloat() y := readFloat() z := readFloat() m := vmath.M4Translate(x, y, z) xform = m.Compose(xform) case "tx": x := readFloat() m := vmath.M4Translate(x, 0, 0) xform = m.Compose(xform) case "ty": y := readFloat() m := vmath.M4Translate(0, y, 0) xform = m.Compose(xform) case "tz": z := readFloat() m := vmath.M4Translate(0, 0, z) xform = m.Compose(xform) case "rx": x := radians(readFloat()) m := vmath.M4RotateX(-x) xform = m.Compose(xform) case "ry": y := radians(readFloat()) m := vmath.M4RotateY(-y) xform = m.Compose(xform) case "rz": z := radians(readFloat()) m := vmath.M4RotateZ(-z) xform = m.Compose(xform) case "": default: fmt.Println("Unknown token: ", token) } if err != nil { break } } (*cache)[s] = *xform return }