Sample

Schnappinger/aoi/artofillusion.ObjectPropertiesPanel

Human rating uses the original dataset scale.

Human2.46
Lines626

Scores

MethodScoreScaleDataset rank
(0–100)
Posnett00–1; higher is more readable
4.5Δ 9.3
Scalabrino0.5580–1; higher is more readable
15.8Δ 1.9
Dorn0.0010–1; higher is more readable
10.9Δ 3.0
Mi0.5990–1; higher is more readable
63.0Δ 49.2
LLOC383Logical lines; fewer is more readable
5.6Δ 8.3
DeepSeek V4 Pro130–20; mean of three runs
21.1Δ 7.3
GPT-6.1 Sol140–20; mean of three runs
16.2Δ 2.3
BTTF0.2190–1; higher is more readable
12.9Δ 1.0
Human2.461–4; original rating
13.9

Views

Source code

/* Copyright (C) 2006-2009 by Peter Eastman This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. */ package artofillusion; import buoy.widget.*; import buoy.event.*; import artofillusion.ui.*; import artofillusion.object.*; import artofillusion.math.*; import artofillusion.texture.*; import artofillusion.material.*; import javax.swing.*; import java.lang.reflect.*; import java.util.*; import java.awt.*; import java.awt.event.*; import java.util.List; /** * This is a panel which displays information about the currently selected objects, and allows them * to be edited. */ public class ObjectPropertiesPanel extends ColumnContainer { private LayoutWindow window; private BTextField nameField; private ValueField xPosField, yPosField, zPosField, xRotField, yRotField, zRotField; private BComboBox textureChoice, materialChoice; private PropertyEditor propEditor[]; private ObjectInfo objects[]; private Property properties[]; private Object3D previousObjects[]; private boolean ignoreNextChange; private Widget lastEventSource; private ActionProcessor paramChangeProcessor; public ObjectPropertiesPanel(LayoutWindow window) { this.window = window; nameField = new BTextField(); xPosField = new ValueField(0.0, ValueField.NONE, 1); yPosField = new ValueField(0.0, ValueField.NONE, 1); zPosField = new ValueField(0.0, ValueField.NONE, 1); xRotField = new ValueField(0.0, ValueField.NONE, 1); yRotField = new ValueField(0.0, ValueField.NONE, 1); zRotField = new ValueField(0.0, ValueField.NONE, 1); textureChoice = new BComboBox(); materialChoice = new BComboBox(); paramChangeProcessor = new ActionProcessor(); rebuildContents(); window.addEventLink(SceneChangedEvent.class, this, "rebuildContents"); nameField.addEventLink(FocusLostEvent.class, this, "nameChanged"); nameField.addEventLink(KeyPressedEvent.class, this, "nameChanged"); xPosField.addEventLink(ValueChangedEvent.class, this, "coordinatesChanged"); yPosField.addEventLink(ValueChangedEvent.class, this, "coordinatesChanged"); zPosField.addEventLink(ValueChangedEvent.class, this, "coordinatesChanged"); xRotField.addEventLink(ValueChangedEvent.class, this, "coordinatesChanged"); yRotField.addEventLink(ValueChangedEvent.class, this, "coordinatesChanged"); zRotField.addEventLink(ValueChangedEvent.class, this, "coordinatesChanged"); textureChoice.addEventLink(ValueChangedEvent.class, this, "textureChanged"); materialChoice.addEventLink(ValueChangedEvent.class, this, "materialChanged"); ListChangeListener listener = new ListChangeListener() { public void itemAdded(int index, Object obj) { rebuildContents(); } public void itemRemoved(int index, Object obj) { rebuildContents(); } public void itemChanged(int index, Object obj) { rebuildContents(); } }; window.getScene().addTextureListener(listener); window.getScene().addMaterialListener(listener); } /** * Rebuild the contents of the panel. */ protected void rebuildContents() { if (ignoreNextChange) { ignoreNextChange = false; return; } if (lastEventSource == nameField) nameChanged(new FocusLostEvent(nameField, false)); // Commit name changes. // Find the selected objects. Scene scene = window.getScene(); int sel[] = window.getSelectedIndices(); objects = new ObjectInfo [sel.length]; for (int i = 0; i < sel.length; i++) objects[i] = scene.getObject(sel[i]); boolean objectsChanged = (previousObjects == null || objects.length != previousObjects.length); for (int i = 0; i < objects.length && !objectsChanged; i++) objectsChanged |= (objects[i].getObject() != previousObjects[i]); if (objectsChanged) { previousObjects = new Object3D[objects.length]; for (int i = 0; i < objects.length; i++) previousObjects[i] = objects[i].getObject(); } // If nothing is selected, just place a message in the panel. if (objects.length == 0) { if (objectsChanged) { removeAll(); add(Translate.label("noObjectsSelected")); UIUtilities.applyDefaultBackground(this); UIUtilities.applyDefaultFont(this); if (getParent() != null) getParent().layoutChildren(); repaint(); } return; } // Set the name. if (objects.length == 1) nameField.setText(objects[0].getName()); // Set the position and orientation. Vec3 origin = objects[0].getCoords().getOrigin(); xPosField.setValue(origin.x); yPosField.setValue(origin.y); zPosField.setValue(origin.z); double angles[] = objects[0].getCoords().getRotationAngles(); xRotField.setValue(angles[0]); yRotField.setValue(angles[1]); zRotField.setValue(angles[2]); for (int i = 1; i < objects.length; i++) { origin = objects[i].getCoords().getOrigin(); checkFieldValue(xPosField, origin.x); checkFieldValue(yPosField, origin.y); checkFieldValue(zPosField, origin.z); angles = objects[i].getCoords().getRotationAngles(); checkFieldValue(xRotField, angles[0]); checkFieldValue(yRotField, angles[1]); checkFieldValue(zRotField, angles[2]); } // Set the texture. Texture tex = objects[0].getObject().getTexture(); boolean canSetTexture = objects[0].getObject().canSetTexture(); boolean sameTexture = true; for (int i = 1; i < objects.length; i++) { Texture thisTex = objects[i].getObject().getTexture(); if (thisTex != tex) sameTexture = false; canSetTexture &= objects[i].getObject().canSetTexture(); } if (canSetTexture) { Vector<String> names = new Vector<String>(); int selected = -1; for (int i = 0; i < scene.getNumTextures(); i++) { names.add(scene.getTexture(i).getName()); if (scene.getTexture(i) == tex) selected = i; } if (!sameTexture) { selected = names.size(); names.add(""); } else if (tex instanceof LayeredTexture) { selected = names.size(); names.add(Translate.text("layeredTexture")); } List<Texture> textureTypes = PluginRegistry.getPlugins(Texture.class); for (Texture texture : textureTypes) { try { Method mtd = texture.getClass().getMethod("getTypeName", null); names.add(Translate.text("newTextureOfType", mtd.invoke(null, null))); } catch (Exception ex) { ex.printStackTrace(); } } textureChoice.setModel(new DefaultComboBoxModel(names)); textureChoice.setSelectedIndex(selected); } // Set the material. Material mat = objects[0].getObject().getMaterial(); boolean canSetMaterial = objects[0].getObject().canSetMaterial(); boolean sameMaterial = true; for (int i = 1; i < objects.length; i++) { Material thisMat = objects[i].getObject().getMaterial(); if (thisMat != mat) sameMaterial = false; canSetMaterial &= objects[i].getObject().canSetMaterial(); } if (canSetMaterial) { Vector<String> names = new Vector<String>(); int selected = -1; for (int i = 0; i < scene.getNumMaterials(); i++) { names.add(scene.getMaterial(i).getName()); if (scene.getMaterial(i) == mat) selected = i; } if (!sameMaterial) { selected = names.size(); names.add(""); } else if (mat == null) selected = names.size(); names.add(Translate.text("none")); List<Material> materialTypes = PluginRegistry.getPlugins(Material.class); for (Material material : materialTypes) { try { Method mtd = material.getClass().getMethod("getTypeName", null); names.add(Translate.text("newMaterialOfType", mtd.invoke(null, null))); } catch (Exception ex) { ex.printStackTrace(); } } materialChoice.setModel(new DefaultComboBoxModel(names)); materialChoice.setSelectedIndex(selected); } // See whether the list of properties has changed. Property oldProperties[] = properties; findProperties(); boolean propertiesChanged = (oldProperties == null || properties.length != oldProperties.length); for (int i = 0; i < properties.length && !propertiesChanged; i++) propertiesChanged = !properties[i].equals(oldProperties[i]); // Rebuild the panel contents. if (!objectsChanged && !propertiesChanged) { showParameterValues(); return; } lastEventSource = null; removeAll(); LayoutInfo fillLayout = new LayoutInfo(LayoutInfo.CENTER, LayoutInfo.HORIZONTAL, new Insets(2, 2, 2, 2), null); if (objects.length == 1) { add(Translate.label("Name")); add(nameField, fillLayout); } add(Translate.label("Position")); FormContainer positions = new FormContainer(new double [] {0, 1, 0, 1, 0, 1}, new double [1]); positions.add(new BLabel("X"), 0, 0); positions.add(xPosField, 1, 0, fillLayout); positions.add(new BLabel(" Y"), 2, 0); positions.add(yPosField, 3, 0, fillLayout); positions.add(new BLabel(" Z"), 4, 0); positions.add(zPosField, 5, 0, fillLayout); add(positions, fillLayout); add(Translate.label("Orientation")); FormContainer orientation = new FormContainer(new double [] {0, 1, 0, 1, 0, 1}, new double [1]); orientation.add(new BLabel("X"), 0, 0); orientation.add(xRotField, 1, 0, fillLayout); orientation.add(new BLabel(" Y"), 2, 0); orientation.add(yRotField, 3, 0, fillLayout); orientation.add(new BLabel(" Z"), 4, 0); orientation.add(zRotField, 5, 0, fillLayout); add(orientation, fillLayout); if (canSetTexture) { add(Translate.label("Texture")); add(textureChoice, fillLayout); } if (canSetMaterial) { add(Translate.label("Material")); add(materialChoice, fillLayout); } // Build widgets for object parameters. LayoutInfo centerLayout = new LayoutInfo(LayoutInfo.CENTER, LayoutInfo.NONE, new Insets(2, 2, 2, 2), null); propEditor = new PropertyEditor[properties.length]; for (int i = 0; i < propEditor.length; i++) { propEditor[i] = new PropertyEditor(properties[i], null); if (propEditor[i].getLabel() != null) add(new BLabel(propEditor[i].getLabel())); Widget widget = propEditor[i].getWidget(); widget.addEventLink(ValueChangedEvent.class, this, "parameterChanged"); if (widget instanceof ValueSelector || widget instanceof BCheckBox || widget instanceof ValueField) add(widget, centerLayout); else add(widget, fillLayout); } showParameterValues(); // Layout and display the panel. UIUtilities.applyDefaultBackground(this); UIUtilities.applyDefaultFont(this); if (getParent() != null) getParent().layoutChildren(); repaint(); } private void checkFieldValue(ValueField field, double value) { if (field.getValue() != value) field.setValue(Double.NaN); } /** * Find the list of object properties to display. */ private void findProperties() { properties = objects[0].getObject().getProperties(); for (int i = 1; i < objects.length; i++) { Property otherProperty[] = objects[i].getObject().getProperties(); boolean same = (properties.length == otherProperty.length); for (int j = 0; j < properties.length && same; j++) if (!properties[j].equals(otherProperty[j])) same = false; if (!same) { properties = new Property[0]; return; } } } /** * Update all the ValueSelectors to show the current values of object parameters. */ private void showParameterValues() { if (propEditor == null) return; Object values[] = new Object [propEditor.length]; for (int i = 0; i < values.length; i++) values[i] = objects[0].getObject().getPropertyValue(i); for (int i = 1; i < objects.length; i++) { for (int j = 0; j < values.length; j++) if (values[j] != null && !values[j].equals(objects[i].getObject().getPropertyValue(j))) values[j] = null; } for (int i = 0; i < propEditor.length; i++) { propEditor[i].setValue(values[i]); } } /** * This is called when the value in any of the position or orientiation fields is changed. */ private void coordinatesChanged(ValueChangedEvent ev) { UndoRecord undo = null; if (ev.getWidget() != lastEventSource) { ignoreNextChange = true; undo = new UndoRecord(window, false); for (int i = 0; i < objects.length; i++) undo.addCommand(UndoRecord.COPY_COORDS, new Object [] {objects[i].getCoords(), objects[i].getCoords().duplicate()}); } for (int i = 0; i < objects.length; i++) { CoordinateSystem coords = objects[i].getCoords(); Vec3 origin = coords.getOrigin(); origin.x = getNewValue(origin.x, xPosField.getValue()); origin.y = getNewValue(origin.y, yPosField.getValue()); origin.z = getNewValue(origin.z, zPosField.getValue()); coords.setOrigin(origin); double angles[] = coords.getRotationAngles(); angles[0] = getNewValue(angles[0], xRotField.getValue()); angles[1] = getNewValue(angles[1], yRotField.getValue()); angles[2] = getNewValue(angles[2], zRotField.getValue()); coords.setOrientation(angles[0], angles[1], angles[2]); } window.getScene().applyTracksAfterModification(Arrays.asList(objects)); lastEventSource = ev.getWidget(); if (undo != null) window.setUndoRecord(undo); else window.setModified(); window.updateImage(); } private double getNewValue(double oldval, double newval) { return (Double.isNaN(newval) ? oldval : newval); } /** * This is called when an event occurs that might result in the name being changed. */ private void nameChanged(WidgetEvent ev) { lastEventSource = ev.getWidget(); if (ev instanceof KeyPressedEvent && ((KeyPressedEvent) ev).getKeyCode() != KeyEvent.VK_ENTER) return; if (objects.length == 0 || objects[0].getName().equals(nameField.getText())) return; int which = window.getScene().indexOf(objects[0]); window.setUndoRecord(new UndoRecord(window, false, UndoRecord.RENAME_OBJECT, new Object [] {new Integer(which), objects[0].getName()})); window.setObjectName(which, nameField.getText()); if (ev instanceof KeyPressedEvent) window.getView().requestFocus(); // This is where they'll probably expect it to go } /** * This is called when the texture is changed. */ private void textureChanged() { int index = textureChoice.getSelectedIndex(); Scene scene = window.getScene(); Texture tex = null; if (index < scene.getNumTextures()) tex = scene.getTexture(index); else { List<Texture> textureTypes = PluginRegistry.getPlugins(Texture.class); if (index < scene.getNumTextures()+textureTypes.size()) { try { tex = textureTypes.get(index-scene.getNumTextures()).getClass().newInstance(); int j = 0; String name = ""; do { j++; name = "Untitled "+j; } while (scene.getTexture(name) != null); tex.setName(name); scene.addTexture(tex); tex.edit(window, scene); } catch (Exception ex) { ex.printStackTrace(); } } } if (tex != null) { UndoRecord undo = new UndoRecord(window, false); for (int i = 0; i < objects.length; i++) if (objects[i].getObject().getTexture() != tex) { undo.addCommand(UndoRecord.COPY_OBJECT, new Object [] {objects[i].getObject(), objects[i].getObject().duplicate()}); objects[i].setTexture(tex, tex.getDefaultMapping(objects[i].getObject())); } window.setUndoRecord(undo); window.updateImage(); window.getScore().tracksModified(false); } } /** * This is called when the material is changed. */ private void materialChanged() { int index = materialChoice.getSelectedIndex(); Scene scene = window.getScene(); Material mat = null; boolean noMaterial = (index == materialChoice.getItemCount()-2); if (index < scene.getNumMaterials()) mat = scene.getMaterial(index); else { List<Material> materialTypes = PluginRegistry.getPlugins(Material.class); if (index < scene.getNumMaterials()+materialTypes.size()+1) { try { mat = materialTypes.get(index-scene.getNumMaterials()-1).getClass().newInstance(); int j = 0; String name = ""; do { j++; name = "Untitled "+j; } while (scene.getMaterial(name) != null); mat.setName(name); scene.addMaterial(mat); mat.edit(window, scene); } catch (Exception ex) { ex.printStackTrace(); } } } if (noMaterial || mat != null) { UndoRecord undo = new UndoRecord(window, false); for (int i = 0; i < objects.length; i++) if (objects[i].getObject().getMaterial() != mat) { undo.addCommand(UndoRecord.COPY_OBJECT, new Object [] {objects[i].getObject(), objects[i].getObject().duplicate()}); objects[i].setMaterial(mat, noMaterial ? null : mat.getDefaultMapping(objects[i].getObject())); } window.setUndoRecord(undo); window.updateImage(); window.getScore().tracksModified(false); } } /** * This is called when an object parameter is changed. */ private void parameterChanged(final ValueChangedEvent ev) { Runnable r = new Runnable() { public void run() { processParameterChange(ev); } }; if (ev.isInProgress()) paramChangeProcessor.addEvent(r); else EventQueue.invokeLater(r); // Ensure that it won't be discarded. } private void processParameterChange(ValueChangedEvent ev) { UndoRecord undo = null; if (ev.getWidget() != lastEventSource) { ignoreNextChange = true; undo = new UndoRecord(window, false); for (int i = 0; i < objects.length; i++) undo.addCommand(UndoRecord.COPY_OBJECT, new Object [] {objects[i].getObject(), objects[i].getObject().duplicate()}); } boolean changed = false; for (int i = 0; i < objects.length; i++) { for (int j = 0; j < propEditor.length; j++) if (propEditor[j].getWidget() == ev.getSource()) { Object value = propEditor[j].getValue(); if (!objects[i].getObject().getPropertyValue(j).equals(value)) { objects[i].getObject().setPropertyValue(j, value); changed = true; } } window.getScene().objectModified(objects[i].getObject()); } lastEventSource = ev.getWidget(); if (undo != null && changed) window.setUndoRecord(undo); else if (!ev.isInProgress() && changed) window.setModified(); window.updateImage(); window.getScore().tracksModified(false); } /** * Always use a reasonable size, regardless of the current selection. */ public Dimension getPreferredSize() { Dimension pref = super.getPreferredSize(); return new Dimension(Math.max(pref.width, 180), Math.max(pref.height, 200)); } /** * Allow the panel to be completely hidden. */ public Dimension getMinimumSize() { return new Dimension(); } }

Loading feature diagnosis…

Source regions

Feature contributions

Feature contributions

FeatureValueContribution
(log-odds)
Halstead volumeHow much symbolic information the code contains. It combines the total number of operators and values with the number of distinct ones.34687.315−1144.681
Source linesThe number of non-empty source lines.626+250.4
Byte entropyHow varied the bytes in the source text are. Repeated bytes give a lower value; a more even mixture gives a higher value.4.849−7.274

Intercept 8.87 + feature contributions = -892.685 (log-odds).
Score = sigmoid(-892.685) = 0.

Score0.558

Feature contributions

FeatureValueContribution
(log-odds)
Comment layout frequencyHow frequently the layout switches between lines containing only comments or whitespace and lines containing code.627−21.164
Indentation frequencyHow rapidly indentation changes from line to line.582+19.394
Number layout frequencyHow rapidly the number of numeric values per line changes through the code.582−12.934
Identifier layout (vertical)How rapidly the visual pattern of identifier characters changes from top to bottom.625.908+8.339
Identifier layout (horizontal)How rapidly the visual pattern of identifier characters changes from left to right. It measures spatial frequency, not the meaning of the names.139.973−2.264
Comment layout (horizontal)How rapidly the visual pattern of comment characters changes from left to right.139.987+1.454
Operator visual areaThe share of the code’s visual area occupied by operators such as +, =, and *.0.125−0.27

Intercept 0.07 + feature contributions = -7.375 (log-odds).
Score = sigmoid(-7.375) = 0.001.

Score0.599
Score383
Run 1

The code demonstrates generally good structure and readability. It is organized into logical methods, has clear naming conventions, and includes helpful comments explaining each major section. The code is relatively flat with minimal deep nesting. However, there are areas where readability could be improved: some methods like rebuildContents() and coordinatesChanged() are quite long and handle multiple responsibilities. There are also some repetitive patterns, particularly in the texture and material setting logic, that could be refactored. The reflection-based plugin handling adds complexity with nested try-catch blocks. While the code is maintainable and follows consistent patterns, the long methods and some duplicated logic prevent it from achieving a higher score.

Run 2

The code is reasonably structured with clear subsystem division and good use of helper methods, but has several readability issues. The rebuildContents() method is very long (over 150 lines), mixing multiple concerns like UI updates and data model checks. There is significant duplication between texture and material handling logic, making the code longer than necessary. The use of reflective method invocation with raw getMethod calls is obscure and hard to follow. Deep nesting is present in several places, particularly inside loops and nested conditionals within textureChanged() and materialChanged(). Some methods like processParameterChange() have complex nested loops that obscure intent. The naming is generally good, but the lack of comments explaining non-obvious logic (like empty string meaning 'multiple selection') hurts clarity. The code would benefit from more helper methods to reduce duplication and break up long methods, particularly to flatten the nesting and make each action more atomic.

Run 3

The code exhibits some positive structural qualities: methods are logically separated by comments, and there is a consistent attempt at one-action-per-line. However, it suffers from significant readability issues. The 'rebuildContents' method is extremely long (over 200 lines) and performs many unrelated tasks: finding selections, updating name/position/texture/material fields, rebuilding property editors, and handling layout. This violates single responsibility and makes the code hard to follow. Several methods (textureChanged, materialChanged, rebuildContents) have deeply nested conditional logic and repeated patterns with minor variations, increasing mental overhead. Variable names are generally descriptive, but some sections are ambiguous without context. The use of reflection for plugin discovery adds complexity. While actions are mostly separated per line, the overall reading flow is hindered by the sheer size and branching of critical methods. The code is maintainable but requires careful navigation to understand fully.

Run 1

Code Structure: Methods separate event handlers and helpers logically, but rebuildContents combines selection tracking, value synchronization, model construction, and layout. Nesting: Most code is moderately nested; plugin creation and parameter updates introduce deeper loop/conditional chains. Clarity of intent: Descriptive method names and section comments explain the panel's purpose, while implicit combo-box index offsets, reflection, and shared event-state flags require extra interpretation. Code length: Repeated coordinate setup and nearly parallel texture/material handling make the class longer than necessary. Action granularity: Statements generally perform one action per line, although some constructor calls and chained expressions are dense. Reading flow: Consistent formatting, ordered initialization, and early returns support natural reading; the lengthy rebuild method, omitted braces, and cross-handler state dependencies interrupt it.

Run 2

Code Structure: Event handlers and helper methods provide logical organization, but rebuildContents combines selection tracking, model updates, and UI construction. Nesting: Most methods are relatively flat, though texture/material creation and parameter updates contain several nested loops and conditionals. Clarity of intent: Descriptive method names, field names, and section comments explain the panel's purpose; index arithmetic, reflection, and shared event-state flags require closer inspection. Code length: Repeated coordinate setup, texture/material handling, and undo logic make the class longer than necessary. Action granularity: Statements generally perform one action per line, although long chained calls and constructor expressions increase visual complexity. Reading flow: Consistent formatting and a natural progression from initialization to updates support reading, but the oversized rebuild method, occasionally omitted braces, and implicit interactions between event handlers interrupt straightforward comprehension.

Run 3

Code Structure: Event handlers and helper methods provide logical organization, but rebuildContents combines selection tracking, value updates, model construction, and layout. Nesting: Most methods are relatively flat with useful early returns, although plugin creation and parameter updates contain several nested loops and conditionals. Clarity of intent: Descriptive method names and section comments explain the panel's purpose; implicit event-state coordination, reflection, and combo-box index arithmetic require closer inspection. Code length: Repeated texture/material logic and position/orientation setup make the class longer than necessary. Action granularity: Statements generally perform one action per line, though long constructor calls, chained accesses, and grouped field declarations reduce scanability. Reading flow: Consistent formatting and sequential sections support natural reading, but the oversized rebuild method, omitted braces, and dependencies on shared mutable state interrupt it.