private static void AddPart(Canvas canvas, Vector parentPos, BodyPart part, Vector pos, double creatureScale, double visualScale, Vector sizeMod, bool centered = false, bool root = false) { #region Shapes Shape partShape; double length; double width; switch (part.OriginalPart.category) { case "NECK": partShape = new Rectangle(); double headDia = VolumeToDiameterConverter.Convert(part.OriginalPart.relsize * creatureScale); foreach (var child in part.Children) { if (child.OriginalPart.category == "HEAD") { headDia = VolumeToDiameterConverter.Convert(child.OriginalPart.relsize * creatureScale); break; } } headDia *= 0.75; double neckArea = Math.PI * (headDia / 2) * (headDia / 2); length = ((part.OriginalPart.relsize * creatureScale) / neckArea) * visualScale; width = headDia * visualScale; break; case "LEG_UPPER": case "LEG_LOWER": case "LEG_FRONT": case "LEG_REAR": case "ARM_UPPER": case "ARM_LOWER": partShape = MakeCylinder(part.OriginalPart, 2.5, creatureScale, visualScale, out width, out length); break; case "TAIL": partShape = MakeCylinder(part.OriginalPart, 5, creatureScale, visualScale, out width, out length); break; case "HORN": case "TUSK": partShape = MakeCylinder(part.OriginalPart, 3, creatureScale, visualScale, out width, out length); break; case "BODY_UPPER": case "BODY_LOWER": case "MANDIBLE": partShape = MakeCylinder(part.OriginalPart, 1, creatureScale, visualScale, out width, out length); break; case "FINGER": partShape = MakeCylinder(part.OriginalPart, 3, creatureScale, visualScale, out width, out length); break; case "RIB_TRUE": case "RIB_FALSE": case "RIB_FLOATING": partShape = MakeCylinder(part.OriginalPart, 10, creatureScale / 24, visualScale, out width, out length); break; case "LIP": partShape = MakeCylinder(part.OriginalPart, 0.2, creatureScale, visualScale, out width, out length); foreach (BodyPartMod mod in part.AppearanceMods) { switch (mod.Original.type) { case "THICKNESS": length *= (mod.CurrentValue / 100.0); break; default: break; } } break; case "TENTACLE": case "ANTENNA": case "ARM": case "LEG": case "STINGER": partShape = MakeCylinder(part.OriginalPart, 5, creatureScale, visualScale, out width, out length); break; case "WING": partShape = new Ellipse(); length = VolumeToDiameterConverter.Convert(part.OriginalPart.relsize * creatureScale) * visualScale * 3; width = length * 2.0 / 3.0; break; case "FIN": case "FLIPPER": case "PINCER": partShape = new Ellipse(); length = VolumeToDiameterConverter.Convert(part.OriginalPart.relsize * creatureScale) * visualScale * 2; width = length * 2.0 / 3.0; break; default: partShape = new Ellipse(); length = VolumeToDiameterConverter.Convert(part.OriginalPart.relsize * creatureScale) * visualScale; width = length; break; } foreach (BodyPartMod mod in part.AppearanceMods) { switch (mod.Original.type) { case "HEIGHT": sizeMod.Y *= (mod.CurrentValue / 100.0); break; case "BROADNESS": sizeMod.X *= (mod.CurrentValue / 100.0); break; case "ROUND_VS_NARROW": sizeMod.X = 0.05 + (mod.CurrentValue * 0.0045); sizeMod.Y = 1; break; default: break; } } width *= sizeMod.X; length *= sizeMod.Y; #endregion partShape.Width = width; partShape.Height = length; partShape.Measure(new Size(1000, 1000)); partShape.Arrange(new Rect(0, 0, 1000, 1000)); Vector localPos = pos - parentPos; RotateTransform rotation = new RotateTransform(-Math.Atan2(localPos.X, localPos.Y) * 180 / Math.PI, partShape.Width / 2, partShape.Height / 2); partShape.RenderTransform = rotation; if (!centered) { var relPos = pos - parentPos; if (relPos.LengthSquared > 0.0001) { relPos.Normalize(); relPos *= (length / 2); pos += relPos; } } Canvas.SetLeft(partShape, (-partShape.Width / 2) + pos.X); Canvas.SetTop(partShape, (-partShape.Height / 2) + pos.Y); #region Colors BodyPartLayer usedLayer = null; foreach (var layer in part.Layers) { // These layer types should never be used to decide the head color. switch (layer.Original.layer_name) { case "SIDEBURNS": case "CHIN_WHISKERS": case "MOUSTACHE": case "EYEBROW": continue; } if (usedLayer == null || layer.Original.layer_depth < usedLayer.Original.layer_depth) // Lower depth = shallower layer. -1 = surface. we take the first non-duplicated layer from the shallowest layers { int duplicates = 0; foreach (var testLayer in part.Layers) { if (layer.Original.layer_depth == testLayer.Original.layer_depth && layer.Original.tissue_id == testLayer.Original.tissue_id) { duplicates++; } } if (duplicates <= 1) { usedLayer = layer; } } } Color fillColor = Color.FromRgb(255, 255, 255); if (usedLayer != null && usedLayer.ColorMod != null) { var color = usedLayer.ColorMod.CurrentPatterns[0].Original.colors[0]; fillColor = Color.FromRgb((byte)color.red, (byte)color.green, (byte)color.blue); } if ((fillColor.R + fillColor.G + fillColor.B) < 15) { partShape.Stroke = new SolidColorBrush(Color.FromRgb(255, 255, 255)); } else { partShape.Stroke = new SolidColorBrush(Color.FromRgb(0, 0, 0)); } partShape.Fill = new SolidColorBrush(fillColor); partShape.ToolTip = part.OriginalPart.token + " - " + part.OriginalPart.category + " - " + part.OriginalPart.relsize; if (usedLayer != null && usedLayer.ColorMod != null) { switch (usedLayer.ColorMod.CurrentPatterns[0].Pattern) { case PatternType.MONOTONE: break; case PatternType.STRIPES: { var stripeBrush = new LinearGradientBrush(); for (int i = 0; i <= 10; i++) { var color = usedLayer.ColorMod.CurrentPatterns[0].Original.colors[i % usedLayer.ColorMod.CurrentPatterns[0].Original.colors.Count]; var stripeColor = Color.FromRgb((byte)color.red, (byte)color.green, (byte)color.blue); stripeBrush.GradientStops.Add(new GradientStop(stripeColor, i / 10.0)); } partShape.Fill = stripeBrush; } break; case PatternType.IRIS_EYE: { var color = usedLayer.ColorMod.CurrentPatterns[0].Original.colors[2]; var irisColor = Color.FromRgb((byte)color.red, (byte)color.green, (byte)color.blue); color = usedLayer.ColorMod.CurrentPatterns[0].Original.colors[1]; var pupilColor = Color.FromRgb((byte)color.red, (byte)color.green, (byte)color.blue); var eyeBrush = new RadialGradientBrush(); eyeBrush.GradientStops.Add(new GradientStop(pupilColor, 0)); eyeBrush.GradientStops.Add(new GradientStop(pupilColor, 0.2)); eyeBrush.GradientStops.Add(new GradientStop(irisColor, 0.21)); eyeBrush.GradientStops.Add(new GradientStop(irisColor, 0.5)); eyeBrush.GradientStops.Add(new GradientStop(fillColor, 0.51)); eyeBrush.GradientStops.Add(new GradientStop(fillColor, 1)); eyeBrush.RadiusX = length / width * 0.5; eyeBrush.RadiusY = 0.5; partShape.Fill = eyeBrush; } break; case PatternType.SPOTS: break; case PatternType.PUPIL_EYE: { var color = usedLayer.ColorMod.CurrentPatterns[0].Original.colors[1]; var pupilColor = Color.FromRgb((byte)color.red, (byte)color.green, (byte)color.blue); var eyeBrush = new RadialGradientBrush(); eyeBrush.GradientStops.Add(new GradientStop(pupilColor, 0)); eyeBrush.GradientStops.Add(new GradientStop(pupilColor, 0.2)); eyeBrush.GradientStops.Add(new GradientStop(fillColor, 0.21)); eyeBrush.GradientStops.Add(new GradientStop(fillColor, 1)); eyeBrush.RadiusX = length / width * 0.5; eyeBrush.RadiusY = 0.5; partShape.Fill = eyeBrush; } break; case PatternType.MOTTLED: break; default: break; } } #endregion Vector direction = (pos - parentPos); if (direction.LengthSquared < 0.0001) { direction = new Vector(0, -1); } direction.Normalize(); #region Categorization List <BodyPart> ExternalParts = new List <BodyPart>(); List <BodyPart> LowerBodyParts = new List <BodyPart>(); List <BodyPart> LeftParts = new List <BodyPart>(); List <BodyPart> RightParts = new List <BodyPart>(); List <BodyPart> EmbeddedParts = new List <BodyPart>(); List <BodyPart> EyeParts = new List <BodyPart>(); List <BodyPart> EyelidParts = new List <BodyPart>(); List <BodyPart> MouthParts = new List <BodyPart>(); List <BodyPart> LipParts = new List <BodyPart>(); List <BodyPart> NoseParts = new List <BodyPart>(); List <BodyPart> CheekParts = new List <BodyPart>(); List <BodyPart> TuskParts = new List <BodyPart>(); List <BodyPart> LeftEarParts = new List <BodyPart>(); List <BodyPart> RightEarParts = new List <BodyPart>(); List <BodyPart> HeadParts = new List <BodyPart>(); List <BodyPart> BackParts = new List <BodyPart>(); List <BodyPart> TailParts = new List <BodyPart>(); List <BodyPart> LeftRibParts = new List <BodyPart>(); List <BodyPart> RightRibParts = new List <BodyPart>(); foreach (var child in part.Children) { if (!child.IsInternal) { if (child.LeadsToHead) { HeadParts.Add(child); } else { switch (child.OriginalPart.category) { case "EYE": EyeParts.Add(child); break; case "MOUTH": case "BEAK": MouthParts.Add(child); break; case "EYELID": EyelidParts.Add(child); break; case "LIP": LipParts.Add(child); break; case "NOSE": NoseParts.Add(child); break; case "CHEEK": CheekParts.Add(child); break; case "TUSK": case "MANDIBLE": TuskParts.Add(child); break; case "EAR": if (child.IsLeft) { LeftEarParts.Add(child); } else { RightEarParts.Add(child); } break; case "SHELL": case "HUMP": BackParts.Add(child); break; case "FIN": if (child.IsLeft && !part.IsLeft) { LeftParts.Add(child); } else if (child.IsRight && !part.IsRight) { RightParts.Add(child); } else { BackParts.Add(child); } break; case "TAIL": TailParts.Add(child); break; case "RIB_TRUE": for (int i = 0; i < 7; i++) { if (child.OriginalPart.token.StartsWith("L_")) { LeftRibParts.Add(child); } else { RightRibParts.Add(child); } } break; case "RIB_FALSE": for (int i = 0; i < 3; i++) { if (child.OriginalPart.token.StartsWith("L_")) { LeftRibParts.Add(child); } else { RightRibParts.Add(child); } } break; case "RIB_FLOATING": for (int i = 0; i < 2; i++) { if (child.OriginalPart.token.StartsWith("L_")) { LeftRibParts.Add(child); } else { RightRibParts.Add(child); } } break; case "TOOTH": case "TONGUE": case "THROAT": break; default: if (child.IsLowerBody) { LowerBodyParts.Add(child); } else if (child.IsEmbedded) { EmbeddedParts.Add(child); } else if (child.IsLeft && !part.IsLeft) { LeftParts.Add(child); } else if (child.IsRight && !part.IsRight) { RightParts.Add(child); } else { ExternalParts.Add(child); } break; } } } } #endregion #region ChildrenCreation for (int i = 0; i < LeftEarParts.Count; i++) { BodyPart child = LeftEarParts[i]; double rotateAngle = 0; double childWidth = 0; foreach (BodyPartMod mod in child.AppearanceMods) { if (mod.Original.type == "SPLAYED_OUT") { childWidth = ((mod.CurrentValue - 100) / 100.0) * VolumeToDiameterConverter.Convert((child.OriginalPart.relsize * creatureScale) * visualScale); } } if (part.IsLowerBody) { if (root) { rotateAngle = Range(i, LeftEarParts.Count, 80, 100); } else { rotateAngle = Range(i, LeftEarParts.Count, -20, -90); } } else { rotateAngle = Range(i, LeftEarParts.Count, 80, 100); } AddPart(canvas, pos, child, pos + (direction * ((width / 2) + childWidth)).Rotate(rotateAngle), creatureScale, visualScale, sizeMod, true); } for (int i = 0; i < RightEarParts.Count; i++) { BodyPart child = RightEarParts[i]; double rotateAngle = 0; double childWidth = 0; foreach (BodyPartMod mod in child.AppearanceMods) { if (mod.Original.type == "SPLAYED_OUT") { childWidth = ((mod.CurrentValue / 200.0) - 0.5) * VolumeToDiameterConverter.Convert((child.OriginalPart.relsize * creatureScale) * visualScale); } } if (part.IsLowerBody) { if (root) { rotateAngle = Range(i, RightEarParts.Count, -80, -100); } else { rotateAngle = Range(i, RightEarParts.Count, 20, 90); } } else { rotateAngle = Range(i, RightEarParts.Count, -80, -100); } AddPart(canvas, pos, child, pos + (direction * ((width / 2) + childWidth)).Rotate(rotateAngle), creatureScale, visualScale, sizeMod, true); } for (int i = 0; i < BackParts.Count; i++) { BodyPart child = BackParts[i]; double rotateAngle = 0; if (part.IsLeft) { rotateAngle = Range(i, BackParts.Count, -45, 45); } else { rotateAngle = Range(i, BackParts.Count, 45, -45); } AddPart(canvas, pos, child, pos + (direction * length / 2).Rotate(rotateAngle), creatureScale, visualScale, sizeMod, true); } for (int i = 0; i < TailParts.Count; i++) { BodyPart child = TailParts[i]; double rotateAngle = Range(i, TailParts.Count, 60, 90); AddPart(canvas, pos, child, pos + (direction * length / 2).Rotate(rotateAngle), creatureScale, visualScale, sizeMod); } //Anything before this is below the part. canvas.Children.Add(partShape); //Anything after this is above the part. for (int i = 0; i < ExternalParts.Count; i++) { BodyPart child = ExternalParts[i]; double rotateAngle = 0; if (part.IsLeft) { rotateAngle = Range(i, ExternalParts.Count, -45, 45); } else { rotateAngle = Range(i, ExternalParts.Count, 45, -45); } AddPart(canvas, pos, child, pos + (direction * length / 2).Rotate(rotateAngle), creatureScale, visualScale, sizeMod); } for (int i = 0; i < HeadParts.Count; i++) { BodyPart child = HeadParts[i]; double rotateAngle = 0; if (part.IsLeft) { rotateAngle = Range(i, HeadParts.Count, -45, 45); } else { rotateAngle = Range(i, HeadParts.Count, 45, -45); } AddPart(canvas, pos, child, pos + (direction * length / 2).Rotate(rotateAngle), creatureScale, visualScale, sizeMod); } for (int i = 0; i < LowerBodyParts.Count; i++) { BodyPart child = LowerBodyParts[i]; double rotateAngle = Range(i, LowerBodyParts.Count, 135, 225); AddPart(canvas, pos, child, pos + (direction * length / 2).Rotate(rotateAngle), creatureScale, visualScale, sizeMod); } for (int i = 0; i < EmbeddedParts.Count; i++) { BodyPart child = EmbeddedParts[i]; Vector childPos = direction; childPos = childPos.Rotate(90); childPos *= Range(i, EmbeddedParts.Count, -length / 4, length / 4); AddPart(canvas, pos, child, pos + childPos, creatureScale, visualScale, sizeMod, true); } for (int i = 0; i < RightRibParts.Count; i++) { BodyPart child = RightRibParts[i]; Vector childPos = (direction.Rotate(90) * width * 0.25) + (direction * Range(i, RightRibParts.Count, length * 0.45, -length * 0.45)); Vector fakeParentPos = direction * Range(i, RightRibParts.Count, length * 0.45, -length * 0.45); AddPart(canvas, fakeParentPos, child, pos + childPos, creatureScale, visualScale, sizeMod, true); } for (int i = 0; i < LeftRibParts.Count; i++) { BodyPart child = LeftRibParts[i]; Vector childPos = (direction.Rotate(-90) * width * 0.25) + (direction * Range(i, LeftRibParts.Count, length * 0.45, -length * 0.45)); Vector fakeParentPos = direction * Range(i, LeftRibParts.Count, length * 0.45, -length * 0.45); AddPart(canvas, fakeParentPos, child, pos + childPos, creatureScale, visualScale, sizeMod, true); } for (int i = 0; i < LeftParts.Count; i++) { BodyPart child = LeftParts[i]; double rotateAngle = 0; if (part.IsLowerBody) { if (root) { rotateAngle = Range(i, LeftParts.Count, 45, 135); } else { rotateAngle = Range(i, LeftParts.Count, -20, -40); } } else { rotateAngle = Range(i, LeftParts.Count, 45, 90); } AddPart(canvas, pos, child, pos + (direction * length / 2).Rotate(rotateAngle), creatureScale, visualScale, sizeMod); } for (int i = 0; i < RightParts.Count; i++) { BodyPart child = RightParts[i]; double rotateAngle = 0; if (part.IsLowerBody) { if (root) { rotateAngle = Range(i, RightParts.Count, -45, -135); } else { rotateAngle = Range(i, RightParts.Count, 20, 40); } } else { rotateAngle = Range(i, RightParts.Count, -45, -90); } AddPart(canvas, pos, child, pos + (direction * length / 2).Rotate(rotateAngle), creatureScale, visualScale, sizeMod); } for (int i = 0; i < EyelidParts.Count; i++) { BodyPart child = EyelidParts[i]; Vector childPos = direction; childPos = childPos.Rotate(90); childPos *= Range(i, EyelidParts.Count, -length * 0.2, length * 0.2); AddPart(canvas, pos, child, pos + childPos + direction * length * 0.1, creatureScale, visualScale, sizeMod, true); } for (int i = 0; i < CheekParts.Count; i++) { BodyPart child = CheekParts[i]; Vector childPos = direction; childPos = childPos.Rotate(90); childPos *= Range(i, CheekParts.Count, -length * 0.2, length * 0.2); AddPart(canvas, pos, child, pos + childPos + direction * length * -0.2, creatureScale, visualScale, sizeMod, true); } for (int i = 0; i < EyeParts.Count; i++) { BodyPart child = EyeParts[i]; double closeSet = 1; foreach (BodyPartMod mod in child.AppearanceMods) { if (mod.Original.type == "CLOSE_SET") { closeSet = (mod.CurrentValue / 100.0) + 0.001; } } Vector childPos = direction; childPos = childPos.Rotate(90); childPos *= Range(i, EyeParts.Count, -length * 0.1, length * 0.1); childPos *= closeSet; AddPart(canvas, pos, child, pos + childPos, creatureScale, visualScale, sizeMod, false); } for (int i = 0; i < MouthParts.Count; i++) { BodyPart child = MouthParts[i]; Vector childPos = direction; childPos = childPos.Rotate(180); childPos.Normalize(); childPos *= length / 2; childPos *= Range(i, MouthParts.Count, 0.75, 0.5); AddPart(canvas, pos, child, pos + childPos, creatureScale, visualScale, sizeMod, true); } for (int i = 0; i < LipParts.Count; i++) { BodyPart child = LipParts[i]; Vector childPos = direction; childPos = childPos.Rotate(180); childPos.Normalize(); childPos *= length / 2; childPos *= Range(i, LipParts.Count, 0.75, 0.6); AddPart(canvas, pos, child, pos + childPos, creatureScale, visualScale, sizeMod, true); } for (int i = 0; i < TuskParts.Count; i++) { BodyPart child = TuskParts[i]; Vector childPos = direction; childPos = childPos.Rotate(90); childPos *= Range(i, TuskParts.Count, -length * 0.2, length * 0.2); AddPart(canvas, pos, child, pos + childPos + direction * length * 0.5 * -0.75, creatureScale, visualScale, sizeMod, true); } for (int i = 0; i < NoseParts.Count; i++) { BodyPart child = NoseParts[i]; Vector childPos = direction; childPos = childPos.Rotate(180); childPos.Normalize(); childPos *= length / 2; childPos *= Range(i, NoseParts.Count, 0.1, 0.5); AddPart(canvas, pos, child, pos + childPos, creatureScale, visualScale, sizeMod, true); } #endregion }
private void treeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs <object> e) { BodyPart part = e.NewValue as BodyPart; bodyPartPropertyGrid.DataContext = part; }