String Styles¶
The StringStyleConfig file controls how each string is drawn — its CAD layer, linetype, draw action, and any additional geometry. Strings are the lines, polylines, or 3D polylines that connect points of the same code and attribute group. The geometry type is set by the Actions value for that code.
The file is located at:
%APPDATA%\SpatialViz\StringStyles\<ConfigName>_StringStyleConfig.txt
Open it from R3MANAGECONFIGS → Edit String Styles.
File format¶
The file has three parts:
- A global default — applied to all strings that have no other rule
- Group blocks — named groups of codes sharing the same layer
- Per-code blocks — individual code overrides with optional conditionals and actions
Global default¶
default => LineType=ByLayer, Actions=Line()
All strings that have no other matching rule use ByLayer linetype and are drawn as connected line segments (Line()). Adjust this if you want a different baseline.
Group blocks¶
group DRAINAGE
include = BC, CHNL, DD, DP, GGP, GR, HW, LN, PI, RAP, SWP, WEIR
Layer = DRAINAGE
All codes in include are drawn on the DRAINAGE layer. This is the most common way to organise string layers.
| Property | Meaning |
|---|---|
group |
Group name (internal only — not written to CAD) |
include |
Comma-separated list of codes belonging to this group |
Layer |
The CAD layer for strings in this group |
LineType |
Optional linetype override (default: ByLayer) |
Actions |
Optional draw action override (default: Line()) |
Long include lists can wrap across indented lines — continuation is implied while the line remains indented:
group ELECTRICAL
include = CAB1, CAB2, CAB3, CABINET, ECABLE, EMARK, EPIT, LIGHT, LIGHT3, LIGHTR, PILLAR, POLE
PYLON, SUB, SWITCH
Layer = ELECTRICAL
Per-code blocks¶
When a single code needs its own layer, linetype, specific actions, or conditional behaviour, write a named block directly (not inside a group):
CODENAME
Layer = LAYERNAME
LineType = LINETYPENAME
Actions = Line()
Conditional rules¶
Use LayerIf, LineTypeIf, and ActionIf to change settings based on attribute values. Conditions use the same syntax as PointStyleConfig:
CODENAME
LayerIf A3 = Top => TOPOGRAPHY-TOP
LayerIf A3 = Bottom => TOPOGRAPHY-BOT
LineTypeIf A3 = Top => DASHED
ActionIf A3 = Top => Polyline()
ActionElse = Line()
Rules are evaluated top-to-bottom; the first match wins for each rule kind (LayerIf, LineTypeIf, ActionIf).
Use && for AND conditions:
KERB
LayerIf A3 = Top && A5 = Mountable => RD-KB-TOP
LayerIf A3 = Top => RD-KB-TOP
LayerIf A3 = Bottom => RD-KB
Use != to match when an attribute does not equal a value:
KERB
ActionIf A3 != Bottom => 3DPolyline()
ActionElse = Line()
ActionElse is the fallback when no ActionIf matches.
Precedence¶
Rules are applied in this order — later entries win over earlier:
- Global default
- Group block (provides layer and default actions for the code)
- Per-code block (most specific — overrides group and global)
Within a per-code block, conditional rules are evaluated top-to-bottom, first match wins per rule kind.
String actions¶
Actions control the geometry drawn for the string. Each action listed below can be used in Actions =, ActionIf ... =>, or ActionElse =.
Baseline geometry¶
These actions draw the primary linework connecting the string's points.
Line() or Line(Dim=2D)¶
Draws the string as individual AutoCAD line segments between consecutive points.
Actions = Line() # 3D lines (default)
Actions = Line(Dim=2D) # flatten to 2D (Z = 0)
Polyline()¶
Draws the string as a single 2D lightweight polyline.
Actions = Polyline()
3DPolyline()¶
Draws the string as a single 3D polyline, preserving all point elevations.
Actions = 3DPolyline()
Note
Polyline() and 3DPolyline() are mutually exclusive with Line(). If either is present, Line() is suppressed automatically to prevent double-drawing.
Offset(DIST, ...)¶
Draws a parallel offset copy of the string at the specified distance.
Offset(DISTANCE, VariableMode=Transition, EraseSource=false, Caps="condition")
| Parameter | Description |
|---|---|
DISTANCE |
Offset distance in drawing units; can be a fixed number or attribute expression like {A4}. Use a negative value to offset to the opposite side. |
VariableMode |
How to handle variable-width strings: Transition (interpolate), First, Last, Mean |
EraseSource |
true removes the original baseline string after offsetting (default: false) |
Caps |
Attribute condition (same syntax as TargetWhere) — draws a short line segment from each matching point to its corresponding vertex on the offset polyline, closing the wall end. Used for corner shots on walls and fences. |
# Fixed 0.5 m offset
Actions = Line(), Offset(0.5)
# Variable offset from A4, erasing the source
ActionIf A4 = * => Offset({A4}, EraseSource=true)
# Wall — offset width from A7, with cap lines at corner shots
WALL
Actions = Polyline()
ActionIf A6 = Right of String => Offset({A7}, Caps="A4=Corner")
ActionIf A6 = Left of String => Offset({A7}*-1, Caps="A4=Corner")
Cap lines are drawn from the surveyed corner point to the corresponding vertex on the offset polyline. This closes the wall end at corners without requiring a separate survey shot at the offset position.
AddArc(EraseSource=true|false)¶
Fits circular arcs through sliding triplets of consecutive points. Used when field crews record intermediate arc shots (three points define an arc).
Actions = AddArc() # keep source lines
Actions = AddArc(EraseSource=true) # replace source lines with arcs
When EraseSource=true, the baseline line segments between arc triplets are removed and replaced by the fitted arcs.
Use with ActionIf to limit arc fitting to specific attribute conditions:
KERB
ActionIf A3 = Arc => AddArc(EraseSource=true)
ActionElse = Line()
AddCircle(EraseSource=true|false)¶
Draws a circle through exactly three points. The three points must not be collinear.
Actions = AddCircle() # keep source lines
Actions = AddCircle(EraseSource=true) # remove source lines
If the three-point circle cannot be formed (collinear, or wrong point count), the action is silently skipped and the baseline is drawn instead.
AddRectangle(Dim=2D|3D, ZMode=..., BestFit=true)¶
Draws a rectangle through 3 or 4 surveyed points. Useful for structures recorded as corner shots.
Actions = AddRectangle()
Actions = AddRectangle(Dim=2D, ZMode=Mean)
Actions = AddRectangle(BestFit=true)
| Parameter | Description |
|---|---|
Dim |
2D (flatten Z) or 3D (preserve elevations). Default: 3D |
ZMode |
How to compute the Z of rectangle vertices: Plane (best-fit plane), Mean (average), Zero, EndPoint, Grade. Default: Plane |
BestFit |
true fits a least-squares rectangle with exactly 90-degree corners. Default: false (parallelogram completion) |
The baseline linework is suppressed when AddRectangle draws successfully.
Standard vs BestFit¶
Standard (default): For 3 points A, B, C — computes the fourth point D as A + C − B (parallelogram completion). The resulting shape is only as square as the survey. For 4 points: closes the shape as-is.
BestFit (BestFit=true): Finds the rectangle with exactly 90-degree corners that minimises the sum of squared distances from the surveyed points to the rectangle corners. Use this when the structure is known to be square but the survey is imprecise — for example chimneys, stormwater pits, columns, or any structure where it is difficult to get a perfect right-angle shot in the field.
- 3 points: tries each point in turn as the right-angle pivot corner. For each candidate, finds the orientation that analytically minimises the squared perpendicular deviations of the two adjacent points from their axes. The configuration with the lowest total residual is used.
- 4 points: uses principal component analysis (PCA) on the point cloud to determine the rectangle's primary axis, then fits the bounding rectangle.
# Chimney — best-fit rectangle, always exactly square
CHIM
Actions = AddRectangle(BestFit=true, Dim=2D)
# Stormwater pit — 3 or 4 shots, ZMode=Mean
PIT
Actions = AddRectangle(BestFit=true, ZMode=Mean)
Z values for the four computed corners are derived using ZMode against a plane fitted to all input points.
AddText(TEXT, ...)¶
Places text labels at the midpoints of qualifying string segments.
AddText("TEXT", Layer=LAYERNAME, Mode=EachSeg, Size=2.5)
| Parameter | Description |
|---|---|
TEXT |
The label string; use {A1}–{A8} placeholders |
Layer |
CAD layer for the text entity (defaults to the string's layer) |
Mode |
Which segments receive a label — see table below |
Size |
Text height in mm on the plotted sheet |
Mode values:
| Mode | Behaviour |
|---|---|
EachSeg |
Label every qualifying segment (default) |
Pairs |
Label only the first and last segment of each qualifying run |
Isolated |
Label only segments with no qualifying neighbours on either side |
FirstOnly |
Label only the first qualifying segment |
# Label every segment of a FENCE string with the fence type
FENCE
Actions = Line()
AddText("{A2} FENCE", Layer=FENCES_LABELS, Mode=Pairs)
# Label only segments where A3 = Gate
FENCE
ActionIf A3 = Gate => AddText("GATE", Layer=FENCES_LABELS, Mode=Isolated)
Actions = Line()
Placeholders {A1}–{A8} are expanded using the attributes of the segment's endpoints.
IntersectWith(...)¶
Extends or trims a string's endpoints to meet another nearby string of the same code. The endpoint adjustments are pre-computed before drawing so draw order does not matter.
IntersectWith(Within=N, Nearest=Mutual, TargetWhere="A3 = Value", AngleMinDeg=30, Dim=3D, ZMode=Plane)
| Parameter | Default | Description |
|---|---|---|
Within |
— | Required. Search radius in drawing units |
Nearest |
Mutual |
Which end to extend: Mutual (both ends), Source (this string), Target (the other string) |
TargetWhere |
any | Filter target strings by attribute conditions (see below) |
AngleMinDeg |
30 |
Minimum intersection angle in degrees — shallow intersections are ignored |
Dim |
3D |
2D or 3D |
ZMode |
Plane |
How to compute Z at the intersection: Plane, Mean, Zero, EndPoint, Grade |
IntersectWith only considers strings of the same code as the source string. You cannot use it to intersect across different codes (e.g. WALL into KERB).
TargetWhere — attribute filtering¶
TargetWhere filters which target string endpoints are eligible for intersection. It uses A1–A8 attribute key=value expressions, the same syntax as ActionIf conditions:
TargetWhere="A3 = Line only"
TargetWhere="A3 = Line only && A5 = Ground"
Use && for AND conditions. TargetWhere does not accept a bare code name — it only works with key = value pairs.
Attribute placeholders {A1}–{A8} are expanded from the source end's attributes, allowing the filter to match targets with the same attribute value as the source:
TargetWhere="A3 = Line only && A5 = {A5}"
This matches targets where A3 = Line only and A5 equals whatever A5 is on the source end — useful when a code has multiple variants (e.g. ground floor vs upper floor) that should only intersect within the same level.
Subject end filtering: TargetWhere is also applied to the source endpoint itself. If the source endpoint does not satisfy the condition, it will not be extended. This means a string that ends with a Corner-coded point will not have that end extended, even if other points in the same string are Line only.
# Buildings — only extend Line only ends, never Corner ends
BLD
LayerIf A5 = Ground => BUILDINGS_G
LayerIf A5 = L1 => BUILDINGS_L1
ActionIf A3 = Line only => IntersectWith(Within=1.5, AngleMinDeg=30, Dim=3D, ZMode=Grade, TargetWhere="A3 = Line only && A5 = {A5}")
In this example:
- Only BLD strings with at least one A3 = Line only point trigger the action
- Only endpoints where A3 = Line only are extended (Corner endpoints are skipped)
- Only targets where A3 = Line only and A5 matches the source level are eligible
- Strings on different levels (e.g. Ground vs L1) never intersect with each other
Full example (from R3Survey2025 configuration)¶
This is the complete StringStyleConfig for the R3Survey2025 configuration. It uses only group blocks — the most common approach.
default => LineType=ByLayer, Actions=Line()
group AIRCON
include = ACON, ADUCT
Layer = AIRCON
group BALCONIES
include = BAL
Layer = BALCONIES
group BUILDINGS
include = BAL, BLD, CE, COL, FFL, FL, GU, OH, RI, ROOM, VER
Layer = BUILDINGS
group BUILD-ELEVS
include = WI, DO
Layer = BUILD-ELEVS
group DRAINAGE
include = BC, CHNL, DD, DP, GGP, GR, HW, LN, PI, RAP, SWP, WEIR
Layer = DRAINAGE
group ELECTRICAL
include = CAB1, CAB2, CAB3, CABINET, ECABLE, EMARK, EPIT, LIGHT, LIGHT3, LIGHTR, PILLAR, POLE
PYLON, SUB, SWITCH
Layer = ELECTRICAL
group FENCES
include = FCE
Layer = FENCES
group VEGETATION
include = TR, TREE
Layer = VEGETATION
Per-code block examples¶
The following examples show the per-code block syntax for codes that need more control than a group block provides:
# Kerb — separate layers for top and bottom, 3D polyline
KERB
LayerIf A3 = Top => RD-KB-TOP
LayerIf A3 = Bottom => RD-KB
Actions = 3DPolyline()
# Building outline — draw as offset pair (wall thickness from A6) and arc where marked
BLD
Layer = BUILDINGS
Actions = Line()
ActionIf A7 = Arc => AddArc(EraseSource=true)
Offset({A6})
# Drainage pipe — label each segment with diameter and material
PI
Layer = DRAINAGE
Actions = Line()
AddText("{A3}%%c {A4} PIPE", Layer=DRAINAGE_LABELS, Mode=Pairs, Size=2.2)
Notes¶
- A code can appear in multiple groups — it is drawn on the last matching group's layer
- Codes not listed in any group or per-code block fall back to the global default
- Linetype
ByLayerinherits from the CAD layer definition — set layer linetypes in the drawing template - String layers must already exist in the drawing or template — R3Survey does not create missing layers
AddRectangle,AddCircle, andAddArcsuppress baseline linework when they draw successfully
Relationship to PointStyleConfig¶
StringStyleConfig controls the string (line, polyline, or 3D polyline); PointStyleConfig controls each point independently. A point and its strings can be on different layers.
For example, KERB points are placed on KERB-PNTS (by PointStyleConfig), while the kerb string is placed on RD-KB (by StringStyleConfig).